1 // Copyright (c) 2015-2019 GeometryFactory (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/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_degeneracies.h $
7 // $Id: repair_degeneracies.h fb6f703 2021-05-04T14:07:49+02:00 Sébastien Loriot
8 // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
9 //
10 // Author(s)     : Sebastien Loriot,
11 //                 Mael Rouxel-Labbé
12 //
13 #ifndef CGAL_POLYGON_MESH_PROCESSING_REPAIR_DEGENERACIES_H
14 #define CGAL_POLYGON_MESH_PROCESSING_REPAIR_DEGENERACIES_H
15 
16 #include <CGAL/license/Polygon_mesh_processing/repair.h>
17 
18 #include <CGAL/Polygon_mesh_processing/shape_predicates.h>
19 #include <CGAL/Polygon_mesh_processing/measure.h>
20 
21 #include <CGAL/boost/graph/selection.h>
22 #include <CGAL/boost/graph/Euler_operations.h>
23 #include <CGAL/boost/graph/Named_function_parameters.h>
24 #include <CGAL/Dynamic_property_map.h>
25 #include <CGAL/property_map.h>
26 #include <CGAL/Union_find.h>
27 
28 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
29 #include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
30 #include <CGAL/IO/OFF.h>
31 #endif
32 
33 #include <boost/algorithm/minmax_element.hpp>
34 #include <boost/utility/enable_if.hpp>
35 
36 #include <algorithm>
37 #include <fstream>
38 #include <iostream>
39 #include <iterator>
40 #include <map>
41 #include <set>
42 #include <sstream>
43 #include <utility>
44 #include <vector>
45 
46 // First part of the file: remove_ALMOST_degenerate_faces (needles/caps)
47 // Second part of the file: remove_degenerate_edges/faces
48 
49 namespace CGAL {
50 namespace Polygon_mesh_processing {
51 namespace internal {
52 
53 template <typename TriangleMesh, typename VPM, typename VCM, typename ECM, typename Traits>
54 std::array<typename boost::graph_traits<TriangleMesh>::halfedge_descriptor, 2>
is_badly_shaped(const typename boost::graph_traits<TriangleMesh>::face_descriptor f,TriangleMesh & tmesh,const VPM & vpm,const VCM & vcm,const ECM & ecm,const Traits & gt,const double cap_threshold,const double needle_threshold,const double collapse_length_threshold)55 is_badly_shaped(const typename boost::graph_traits<TriangleMesh>::face_descriptor f,
56                 TriangleMesh& tmesh,
57                 const VPM& vpm,
58                 const VCM& vcm,
59                 const ECM& ecm,
60                 const Traits& gt,
61                 const double cap_threshold, // angle over 160° ==> cap
62                 const double needle_threshold, // longest edge / shortest edge over this ratio ==> needle
63                 const double collapse_length_threshold) // max length of edges allowed to be collapsed
64 {
65   namespace PMP = CGAL::Polygon_mesh_processing;
66 
67   typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor       halfedge_descriptor;
68 
69   const halfedge_descriptor null_h = boost::graph_traits<TriangleMesh>::null_halfedge();
70 
71   halfedge_descriptor res = PMP::is_needle_triangle_face(f, tmesh, needle_threshold,
72                                                          parameters::vertex_point_map(vpm)
73                                                                     .geom_traits(gt));
74   if(res != null_h && (!get(vcm, source(res, tmesh)) || !get(vcm, target(res, tmesh))) )
75   {
76     // don't want to collapse edges that are too large
77     if(collapse_length_threshold == 0 ||
78        edge_length(res, tmesh, parameters::vertex_point_map(vpm).geom_traits(gt)) <= collapse_length_threshold)
79     {
80       return make_array(res, null_h);
81     }
82   }
83 
84   res = PMP::is_cap_triangle_face(f, tmesh, cap_threshold, parameters::vertex_point_map(vpm).geom_traits(gt));
85   if(res != null_h && !get(ecm, edge(res, tmesh)))
86     return make_array(null_h, res);
87 
88   return make_array(null_h, null_h);
89 }
90 
91 template <typename TriangleMesh, typename HalfedgeContainer,
92           typename VPM, typename VCM, typename ECM, typename Traits>
collect_badly_shaped_triangles(const typename boost::graph_traits<TriangleMesh>::face_descriptor f,TriangleMesh & tmesh,const VPM & vpm,const VCM & vcm,const ECM & ecm,const Traits & gt,const double cap_threshold,const double needle_threshold,const double collapse_length_threshold,HalfedgeContainer & edges_to_collapse,HalfedgeContainer & edges_to_flip)93 void collect_badly_shaped_triangles(const typename boost::graph_traits<TriangleMesh>::face_descriptor f,
94                                     TriangleMesh& tmesh,
95                                     const VPM& vpm,
96                                     const VCM& vcm,
97                                     const ECM& ecm,
98                                     const Traits& gt,
99                                     const double cap_threshold, // angle over this threshold (as a cosine) ==> cap
100                                     const double needle_threshold, // longest edge / shortest edge over this ratio ==> needle
101                                     const double collapse_length_threshold, // max length of edges allowed to be collapsed
102                                     HalfedgeContainer& edges_to_collapse,
103                                     HalfedgeContainer& edges_to_flip)
104 {
105   typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor       halfedge_descriptor;
106 
107   std::array<halfedge_descriptor, 2> res = is_badly_shaped(f, tmesh, vpm, vcm, ecm, gt, cap_threshold,
108                                                            needle_threshold, collapse_length_threshold);
109 
110   if(res[0] != boost::graph_traits<TriangleMesh>::null_halfedge())
111   {
112 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA
113     std::cout << "add new needle: " << edge(res[0], tmesh) << std::endl;
114 #endif
115     CGAL_assertion(!is_border(res[0], tmesh));
116     CGAL_assertion(!get(ecm, edge(res[0], tmesh)));
117     edges_to_collapse.insert(res[0]);
118   }
119   else // let's not make it possible to have a face be both a cap and a needle (for now)
120   {
121     if(res[1] != boost::graph_traits<TriangleMesh>::null_halfedge())
122     {
123 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA
124       std::cout << "add new cap: " << edge(res[1],tmesh) << std::endl;
125 #endif
126       CGAL_assertion(!is_border(res[1], tmesh));
127       CGAL_assertion(!get(ecm, edge(res[1], tmesh)));
128       edges_to_flip.insert(res[1]);
129     }
130   }
131 }
132 
133 /*
134 // Following Ronfard et al. 96 we look at variation of the normal after the collapse
135 // the collapse must be topologically valid
136 template <class TriangleMesh, class NamedParameters>
137 bool is_collapse_geometrically_valid(typename boost::graph_traits<TriangleMesh>::halfedge_descriptor h,
138                                      const TriangleMesh& tmesh,
139                                      const NamedParameters& np)
140 {
141   using CGAL::parameters::choose_parameter;
142   using CGAL::parameters::get_parameter;
143 
144   typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor         halfedge_descriptor;
145   typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::const_type   VPM;
146   typedef typename boost::property_traits<VPM>::reference                         Point_ref;
147   typedef typename GetGeomTraits<TriangleMesh, NamedParameters>::type             Traits;
148 
149   VPM vpm = choose_parameter(get_parameter(np, internal_np::vertex_point),
150                              get_const_property_map(vertex_point, tmesh));
151   Traits gt = choose_parameter(get_parameter(np, internal_np::geom_traits), Traits());
152 
153   // @todo handle boundary edges
154 
155   h = opposite(h, tmesh); // Euler::collapse edge keeps the target and removes the source
156 
157   // source is kept, target is removed
158   CGAL_assertion(target(h, tmesh) == vertex_removed);
159   Point_ref kept = get(vpm, source(h, tmesh));
160   Point_ref removed= get(vpm, target(h, tmesh));
161 
162   // consider triangles incident to the vertex removed
163   halfedge_descriptor stop = prev(opposite(h, tmesh), tmesh);
164   halfedge_descriptor hi = opposite(next(h, tmesh), tmesh);
165 
166   std::vector<halfedge_descriptor> triangles;
167   while(hi != stop)
168   {
169     if(!is_border(hi, tmesh))
170     {
171       Point_ref a = get(vpm, target(next(hi, tmesh), tmesh));
172       Point_ref b = get(vpm, source(hi, tmesh));
173 
174       //ack a-b-point_remove and a-b-point_kept has a compatible orientation
175       // @todo use a predicate
176       typename Traits::Vector_3 n1 = gt.construct_cross_product_vector_3_object()(removed-a, b-a);
177       typename Traits::Vector_3 n2 = gt.construct_cross_product_vector_3_object()(kept-a, b-a);
178       if(gt.compute_scalar_product_3_object()(n1, n2) <= 0)
179         return false;
180     }
181 
182     hi = opposite(next(hi, tmesh), tmesh);
183   }
184 
185   return true;
186 }
187 */
188 
189 template <class TriangleMesh, typename VPM, typename Traits>
190 boost::optional<typename Traits::FT>
get_collapse_volume(typename boost::graph_traits<TriangleMesh>::halfedge_descriptor h,const TriangleMesh & tmesh,const VPM & vpm,const Traits & gt)191 get_collapse_volume(typename boost::graph_traits<TriangleMesh>::halfedge_descriptor h,
192                     const TriangleMesh& tmesh,
193                     const VPM& vpm,
194                     const Traits& gt)
195 {
196   typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor         halfedge_descriptor;
197 
198   typedef typename Traits::FT                                                     FT;
199   typedef typename boost::property_traits<VPM>::reference                         Point_ref;
200   typedef typename Traits::Vector_3                                               Vector_3;
201 
202   const typename Traits::Point_3 origin(ORIGIN);
203 
204 // @todo handle boundary edges
205 
206   h = opposite(h, tmesh); // Euler::collapse edge keeps the target and removes the source
207 
208   // source is kept, target is removed
209   Point_ref kept = get(vpm, source(h, tmesh));
210   Point_ref removed= get(vpm, target(h, tmesh));
211 
212   // init volume with incident triangles (reversed orientation
213   FT delta_vol = volume(removed, kept, get(vpm, target(next(h, tmesh), tmesh)), origin) +
214                  volume(kept, removed, get(vpm, target(next(opposite(h, tmesh), tmesh), tmesh)), origin);
215 
216   // consider triangles incident to the vertex removed
217   halfedge_descriptor stop = prev(opposite(h, tmesh), tmesh);
218   halfedge_descriptor hi = opposite(next(h, tmesh), tmesh);
219 
220   std::vector<halfedge_descriptor> triangles;
221   while(hi != stop)
222   {
223     if(!is_border(hi, tmesh))
224     {
225       Point_ref a = get(vpm, target(next(hi, tmesh), tmesh));
226       Point_ref b = get(vpm, source(hi, tmesh));
227 
228       //ack a-b-point_remove and a-b-point_kept has a compatible orientation
229       // @todo use a predicate
230       Vector_3 v_ab = gt.construct_vector_3_object()(a, b);
231       Vector_3 v_ar = gt.construct_vector_3_object()(a, removed);
232       Vector_3 v_ak = gt.construct_vector_3_object()(a, kept);
233 
234       Vector_3 n1 = gt.construct_cross_product_vector_3_object()(v_ar, v_ab);
235       Vector_3 n2 = gt.construct_cross_product_vector_3_object()(v_ak, v_ab);
236       if(gt.compute_scalar_product_3_object()(n1, n2) <= 0)
237         return boost::none;
238 
239       delta_vol += volume(b, a, removed, origin) + volume(a, b, kept, origin); // opposite orientation
240     }
241 
242     hi = opposite(next(hi, tmesh), tmesh);
243   }
244 
245   return CGAL::abs(delta_vol);
246 }
247 
248 template <typename TriangleMesh, typename VPM, typename VCM, typename Traits>
249 typename boost::graph_traits<TriangleMesh>::halfedge_descriptor
get_best_edge_orientation(typename boost::graph_traits<TriangleMesh>::edge_descriptor e,const TriangleMesh & tmesh,const VPM & vpm,const VCM & vcm,const Traits & gt)250 get_best_edge_orientation(typename boost::graph_traits<TriangleMesh>::edge_descriptor e,
251                           const TriangleMesh& tmesh,
252                           const VPM& vpm,
253                           const VCM& vcm,
254                           const Traits& gt)
255 {
256   typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
257   typedef typename Traits::FT                                             FT;
258 
259   halfedge_descriptor h = halfedge(e, tmesh), ho = opposite(h, tmesh);
260 
261   boost::optional<FT> dv1 = get_collapse_volume(h, tmesh, vpm, gt);
262   boost::optional<FT> dv2 = get_collapse_volume(ho, tmesh, vpm, gt);
263 
264   // the resulting point of the collapse of a halfedge is the target of the halfedge before collapse
265   if(get(vcm, source(h, tmesh)))
266      return dv2 != boost::none ? ho
267                                : boost::graph_traits<TriangleMesh>::null_halfedge();
268 
269   if(get(vcm, target(h, tmesh)))
270      return dv1 != boost::none ? h
271                                : boost::graph_traits<TriangleMesh>::null_halfedge();
272 
273   if(dv1 != boost::none)
274   {
275     if(dv2 != boost::none)
276       return (*dv1 < *dv2) ? h : ho;
277 
278     return h;
279   }
280 
281   if(dv2 != boost::none)
282     return ho;
283 
284   return boost::graph_traits<TriangleMesh>::null_halfedge();
285 }
286 
287 // adapted from triangulate_faces
288 template <typename TriangleMesh, typename VPM, typename Traits>
should_flip(typename boost::graph_traits<TriangleMesh>::edge_descriptor e,const TriangleMesh & tmesh,const VPM & vpm,const Traits & gt)289 bool should_flip(typename boost::graph_traits<TriangleMesh>::edge_descriptor e,
290                  const TriangleMesh& tmesh,
291                  const VPM& vpm,
292                  const Traits& gt)
293 {
294   typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
295 
296   typedef typename Traits::FT                                             FT;
297   typedef typename boost::property_traits<VPM>::reference                 Point_ref;
298   typedef typename Traits::Vector_3                                       Vector_3;
299 
300   CGAL_precondition(!is_border(e, tmesh));
301 
302   halfedge_descriptor h = halfedge(e, tmesh);
303 
304   Point_ref p0 = get(vpm, target(h, tmesh));
305   Point_ref p1 = get(vpm, target(next(h, tmesh), tmesh));
306   Point_ref p2 = get(vpm, source(h, tmesh));
307   Point_ref p3 = get(vpm, target(next(opposite(h, tmesh), tmesh), tmesh));
308 
309   /* Chooses the diagonal that will split the quad in two triangles that maximize
310    * the scalar product of of the un-normalized normals of the two triangles.
311    * The lengths of the un-normalized normals (computed using cross-products of two vectors)
312    *  are proportional to the area of the triangles.
313    * Maximize the scalar product of the two normals will avoid skinny triangles,
314    * and will also taken into account the cosine of the angle between the two normals.
315    * In particular, if the two triangles are oriented in different directions,
316    * the scalar product will be negative.
317    */
318 
319 //  CGAL::cross_product(p2-p1, p3-p2) * CGAL::cross_product(p0-p3, p1-p0);
320 //  CGAL::cross_product(p1-p0, p1-p2) * CGAL::cross_product(p3-p2, p3-p0);
321 
322   const Vector_3 v01 = gt.construct_vector_3_object()(p0, p1);
323   const Vector_3 v12 = gt.construct_vector_3_object()(p1, p2);
324   const Vector_3 v23 = gt.construct_vector_3_object()(p2, p3);
325   const Vector_3 v30 = gt.construct_vector_3_object()(p3, p0);
326 
327   const FT p1p3 = gt.compute_scalar_product_3_object()(
328                     gt.construct_cross_product_vector_3_object()(v12, v23),
329                     gt.construct_cross_product_vector_3_object()(v30, v01));
330 
331   const Vector_3 v21 = gt.construct_opposite_vector_3_object()(v12);
332   const Vector_3 v03 = gt.construct_opposite_vector_3_object()(v30);
333 
334   const FT p0p2 = gt.compute_scalar_product_3_object()(
335                     gt.construct_cross_product_vector_3_object()(v01, v21),
336                     gt.construct_cross_product_vector_3_object()(v23, v03));
337 
338   return p0p2 <= p1p3;
339 }
340 
341 } // namespace internal
342 
343 namespace experimental {
344 
345 // @todo check what to use as priority queue with removable elements, set might not be optimal
346 template <typename FaceRange, typename TriangleMesh, typename NamedParameters>
remove_almost_degenerate_faces(const FaceRange & face_range,TriangleMesh & tmesh,const double cap_threshold,const double needle_threshold,const double collapse_length_threshold,const NamedParameters & np)347 bool remove_almost_degenerate_faces(const FaceRange& face_range,
348                                     TriangleMesh& tmesh,
349                                     const double cap_threshold,
350                                     const double needle_threshold,
351                                     const double collapse_length_threshold,
352                                     const NamedParameters& np)
353 {
354   using CGAL::parameters::choose_parameter;
355   using CGAL::parameters::get_parameter;
356 
357   typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor         vertex_descriptor;
358   typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor       halfedge_descriptor;
359   typedef typename boost::graph_traits<TriangleMesh>::edge_descriptor           edge_descriptor;
360   typedef typename boost::graph_traits<TriangleMesh>::face_descriptor           face_descriptor;
361 
362   typedef Static_boolean_property_map<vertex_descriptor, false>                 Default_VCM;
363   typedef typename internal_np::Lookup_named_param_def<internal_np::vertex_is_constrained_t,
364                                                        NamedParameters,
365                                                        Default_VCM>::type       VCM;
366   VCM vcm_np = choose_parameter(get_parameter(np, internal_np::vertex_is_constrained), Default_VCM());
367 
368   typedef Static_boolean_property_map<edge_descriptor, false>                   Default_ECM;
369   typedef typename internal_np::Lookup_named_param_def<internal_np::edge_is_constrained_t,
370                                                        NamedParameters,
371                                                        Default_ECM>::type       ECM;
372   ECM ecm = choose_parameter(get_parameter(np, internal_np::edge_is_constrained), Default_ECM());
373 
374   typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::const_type VPM;
375   VPM vpm = choose_parameter(get_parameter(np, internal_np::vertex_point),
376                              get_const_property_map(vertex_point, tmesh));
377 
378   typedef typename GetGeomTraits<TriangleMesh, NamedParameters>::type           Traits;
379   Traits gt = choose_parameter(get_parameter(np, internal_np::geom_traits), Traits());
380 
381   // Vertex property map that combines the VCM and the fact that extremities of a constrained edge should be constrained
382   typedef CGAL::dynamic_vertex_property_t<bool>                                 Vertex_property_tag;
383   typedef typename boost::property_map<TriangleMesh, Vertex_property_tag>::type DVCM;
384   DVCM vcm = get(Vertex_property_tag(), tmesh);
385 
386   CGAL_precondition(is_valid_polygon_mesh(tmesh));
387   CGAL_precondition(is_triangle_mesh(tmesh));
388 
389   for(face_descriptor f : face_range)
390   {
391     if(f == boost::graph_traits<TriangleMesh>::null_face())
392       continue;
393 
394     for(halfedge_descriptor h : CGAL::halfedges_around_face(halfedge(f, tmesh), tmesh))
395     {
396       if(get(ecm, edge(h, tmesh)))
397       {
398         put(vcm, source(h, tmesh), true);
399         put(vcm, target(h, tmesh), true);
400       }
401       else if(get(vcm_np, target(h, tmesh)))
402       {
403         put(vcm, target(h, tmesh), true);
404       }
405     }
406   }
407 
408   // Start the process of removing bad elements
409   std::set<halfedge_descriptor> edges_to_collapse;
410   std::set<halfedge_descriptor> edges_to_flip;
411 
412   // @todo could probably do something a bit better by looping edges, consider the incident faces
413   // f1 / f2 and look at f1 if f1<f2, and the edge is smaller than the two other edges...
414   for(face_descriptor f : face_range)
415   {
416     internal::collect_badly_shaped_triangles(f, tmesh, vpm, vcm, ecm, gt,
417                                              cap_threshold, needle_threshold, collapse_length_threshold,
418                                              edges_to_collapse, edges_to_flip);
419   }
420 
421 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES
422   std::cout << edges_to_collapse.size() << " to collapse" << std::endl;
423   std::cout << edges_to_flip.size() << " to flip" << std::endl;
424 #endif
425 
426 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES
427   int iter = 0;
428 #endif
429 
430   for(;;)
431   {
432     bool something_was_done = false;
433 
434 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES
435     std::cout << edges_to_collapse.size() << " needles and " << edges_to_flip.size() << " caps" << std::endl;
436     std::cout << "Iter: " << iter << std::endl;
437     std::ostringstream oss;
438     oss << "degen_cleaning_iter_" << iter++ << ".off";
439     CGAL::IO::write_polygon_mesh(oss.str(), tmesh, CGAL::parameters::stream_precision(17));
440 #endif
441 
442     if(edges_to_collapse.empty() && edges_to_flip.empty())
443       return true;
444 
445     // @todo maybe using a priority queue handling the more almost degenerate elements should be used
446     std::set<halfedge_descriptor> next_edges_to_collapse;
447     std::set<halfedge_descriptor> next_edges_to_flip;
448 
449     // Treat needles ===============================================================================
450 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA
451     int kk=0;
452     std::ofstream(std::string("tmp/n-00000.off")) << tmesh;
453 #endif
454     while(!edges_to_collapse.empty())
455     {
456       halfedge_descriptor h = *edges_to_collapse.begin();
457       edges_to_collapse.erase(edges_to_collapse.begin());
458 
459       CGAL_assertion(!is_border(h, tmesh));
460 
461       const edge_descriptor e = edge(h, tmesh);
462       CGAL_assertion(!get(ecm, edge(h, tmesh)));
463 
464       if(get(vcm, source(h, tmesh)) && get(vcm, target(h, tmesh)))
465         continue;
466 
467 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA
468       std::cout << "  treat needle: " << e
469                 << " (" << source(e, tmesh) << " " << tmesh.point(source(h, tmesh))
470                 << " --- " << source(e, tmesh) << " " << tmesh.point(target(h, tmesh)) << ")" << std::endl;
471 #endif
472       if(CGAL::Euler::does_satisfy_link_condition(e, tmesh))
473       {
474         // Verify that the element is still badly shaped
475         const std::array<halfedge_descriptor, 2> nc =
476           internal::is_badly_shaped(face(h, tmesh), tmesh, vpm, vcm, ecm, gt,
477                                     cap_threshold, needle_threshold, collapse_length_threshold);
478 
479         if(nc[0] != h)
480         {
481 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA
482           std::cout << "\t Needle criteria no longer verified" << std::endl;
483 #endif
484           continue;
485         }
486 
487         // pick the orientation of edge to keep the vertex minimizing the volume variation
488         const halfedge_descriptor best_h = internal::get_best_edge_orientation(e, tmesh, vpm, vcm, gt);
489         if(best_h == boost::graph_traits<TriangleMesh>::null_halfedge())
490         {
491 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA
492             std::cout << "\t Geometrically invalid edge collapse!" << std::endl;
493 #endif
494           next_edges_to_collapse.insert(h);
495           continue;
496         }
497 
498         // Proceeding with the collapse, purge the sets from halfedges being removed
499         for(int i=0; i<2; ++i)
500         {
501           if(!is_border(h, tmesh))
502           {
503             edges_to_flip.erase(h);
504             edges_to_collapse.erase(h);
505             next_edges_to_collapse.erase(h);
506 
507             halfedge_descriptor rm_h = prev(h, tmesh);
508             if(get(ecm, edge(rm_h, tmesh)))
509               rm_h = next(h, tmesh);
510 
511             edges_to_flip.erase(rm_h);
512             edges_to_collapse.erase(rm_h);
513             next_edges_to_collapse.erase(rm_h);
514 
515             halfedge_descriptor opp_rm_h = opposite(rm_h, tmesh);
516             edges_to_flip.erase(opp_rm_h);
517             edges_to_collapse.erase(opp_rm_h);
518             next_edges_to_collapse.erase(opp_rm_h);
519           }
520 
521           h = opposite(h, tmesh);
522         }
523 
524 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA
525         std::cout << "  " << kk << " -- Collapsing " << tmesh.point(source(best_h, tmesh)) << "  "
526                                                      << tmesh.point(target(best_h, tmesh)) << std::endl;
527 #endif
528 
529         CGAL_assertion(!get(vcm, source(best_h, tmesh)));
530 
531         // The function get_best_edge_orientation() has ensured that get(vcm, source(h, tmesh))
532         // is not constrained, so get(ecm, e) is also not constrained for all e incident
533         // to source(h, tmesh).
534         //
535         // The function Euler::collapse_edge() removes edge(prev(h, tmesh)), but that edge
536         // might be constrained. In that case, next() must be removed instead.
537         vertex_descriptor v;
538         if(get(ecm, edge(prev(h, tmesh), tmesh)))
539           v = Euler::collapse_edge(edge(best_h, tmesh), tmesh, ecm);
540         else
541           v = Euler::collapse_edge(edge(best_h, tmesh), tmesh);
542 
543         // moving to the midpoint is not a good idea. On a circle for example you might endpoint with
544         // a bad geometry because you iteratively move one point
545         // auto mp = midpoint(tmesh.point(source(h, tmesh)), tmesh.point(target(h, tmesh)));
546         // tmesh.point(v) = mp;
547 
548         // examine all faces incident to the vertex kept
549         for(halfedge_descriptor hv : halfedges_around_target(v, tmesh))
550         {
551           if(!is_border(hv, tmesh))
552           {
553             internal::collect_badly_shaped_triangles(face(hv, tmesh), tmesh, vpm, vcm, ecm, gt,
554                                                      cap_threshold, needle_threshold, collapse_length_threshold,
555                                                      edges_to_collapse, edges_to_flip);
556           }
557         }
558 
559 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA
560         std::string nb = std::to_string(++kk);
561         if(kk<10) nb = std::string("0")+nb;
562         if(kk<100) nb = std::string("0")+nb;
563         if(kk<1000) nb = std::string("0")+nb;
564         if(kk<10000) nb = std::string("0")+nb;
565         std::ofstream(std::string("tmp/n-")+nb+std::string(".off")) << tmesh;
566 #endif
567         something_was_done = true;
568       }
569       else // ! CGAL::Euler::does_satisfy_link_condition(e, tmesh)
570       {
571 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA
572         std::cout << "\t Uncollapsable edge!" << std::endl;
573 #endif
574         next_edges_to_collapse.insert(h);
575       }
576     }
577 
578     // Treat caps ==================================================================================
579     CGAL_assertion(next_edges_to_flip.empty());
580 
581 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA
582     kk=0;
583     std::ofstream(std::string("tmp/c-000.off")) << tmesh;
584 #endif
585     while(!edges_to_flip.empty())
586     {
587       halfedge_descriptor h = *edges_to_flip.begin();
588       edges_to_flip.erase(edges_to_flip.begin());
589 
590       CGAL_assertion(!is_border(h, tmesh));
591 
592       const edge_descriptor e = edge(h, tmesh);
593       CGAL_assertion(!get(ecm, e));
594 
595 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA
596       std::cout << "  treat cap: " << e
597                 << " (" << source(e, tmesh) << " " << tmesh.point(source(h, tmesh))
598                 << " --- " << target(e, tmesh) << " " << tmesh.point(target(h, tmesh)) << ")" << std::endl;
599 #endif
600 
601       std::array<halfedge_descriptor,2> nc = internal::is_badly_shaped(face(h, tmesh), tmesh, vpm, vcm, ecm, gt,
602                                                                        cap_threshold, needle_threshold,
603                                                                        collapse_length_threshold);
604       // Check the triangle is still a cap
605       if(nc[1] != h)
606       {
607 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA
608         std::cout << "\t Cap criteria no longer verified" << std::endl;
609 #endif
610         continue;
611       }
612 
613       // special case of `edge(h, tmesh)` being a border edge --> remove the face
614       if(is_border(opposite(h, tmesh), tmesh))
615       {
616         for(halfedge_descriptor hh : CGAL::halfedges_around_face(h, tmesh))
617         {
618           // Remove from even 'next_edges_to_flip' because it might have been re-added from a flip
619           edges_to_flip.erase(hh);
620           next_edges_to_flip.erase(hh);
621           next_edges_to_collapse.erase(hh);
622         }
623 
624         Euler::remove_face(h, tmesh);
625 
626         something_was_done = true;
627         continue;
628       }
629 
630       CGAL_assertion(!is_border(e, tmesh));
631 
632       // condition for the flip to be valid (the edge to be created does not already exist)
633       if(!halfedge(target(next(h, tmesh), tmesh),
634                    target(next(opposite(h, tmesh), tmesh), tmesh), tmesh).second)
635       {
636         if(!internal::should_flip(e, tmesh, vpm, gt))
637         {
638 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA
639           std::cout << "\t Flipping prevented: not the best diagonal" << std::endl;
640 #endif
641           next_edges_to_flip.insert(h);
642           continue;
643         }
644 
645 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA
646         std::cout << "\t step " << kk << " -- Flipping" << std::endl;
647 #endif
648         Euler::flip_edge(h, tmesh);
649         CGAL_assertion(edge(h, tmesh) == e);
650 
651         // handle face updates
652         for(int i=0; i<2; ++i)
653         {
654           CGAL_assertion(!is_border(h, tmesh));
655           std::array<halfedge_descriptor, 2> nc =
656             internal::is_badly_shaped(face(h, tmesh), tmesh, vpm, vcm, ecm, gt,
657                                       cap_threshold, needle_threshold, collapse_length_threshold);
658 
659           if(nc[1] != boost::graph_traits<TriangleMesh>::null_halfedge() && nc[1] != h)
660             next_edges_to_flip.insert(nc[1]);
661           else if(nc[0] != boost::graph_traits<TriangleMesh>::null_halfedge())
662             next_edges_to_collapse.insert(nc[0]);
663 
664           h = opposite(h, tmesh);
665         }
666 
667         something_was_done = true;
668       }
669       else // flipped edge already exists in the mesh
670       {
671 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA
672         std::cout << "\t Unflippable edge!" << std::endl;
673 #endif
674         CGAL_assertion(!is_border(h, tmesh));
675         next_edges_to_flip.insert(h);
676       }
677 
678 #ifdef CGAL_PMP_DEBUG_REMOVE_DEGENERACIES_EXTRA
679       std::string nb = std::to_string(++kk);
680       if(kk<10) nb = std::string("0")+nb;
681       if(kk<100) nb = std::string("0")+nb;
682       if(kk<1000) nb = std::string("0")+nb;
683       if(kk<10000) nb = std::string("0")+nb;
684       std::ofstream(std::string("tmp/c-")+nb+std::string(".off")) << tmesh;
685 #endif
686     }
687 
688     if(!something_was_done)
689       return false;
690 
691     std::swap(edges_to_collapse, next_edges_to_collapse);
692     std::swap(edges_to_flip, next_edges_to_flip);
693   }
694 
695   return false;
696 }
697 
698 template <typename FaceRange, typename TriangleMesh>
remove_almost_degenerate_faces(const FaceRange & face_range,TriangleMesh & tmesh,const double cap_threshold,const double needle_threshold,const double collapse_length_threshold)699 bool remove_almost_degenerate_faces(const FaceRange& face_range,
700                                     TriangleMesh& tmesh,
701                                     const double cap_threshold,
702                                     const double needle_threshold,
703                                     const double collapse_length_threshold)
704 {
705   return remove_almost_degenerate_faces(face_range, tmesh,
706                                         cap_threshold, needle_threshold, collapse_length_threshold,
707                                         CGAL::parameters::all_default());
708 }
709 
710 template <typename TriangleMesh, typename CGAL_PMP_NP_TEMPLATE_PARAMETERS>
remove_almost_degenerate_faces(TriangleMesh & tmesh,const double cap_threshold,const double needle_threshold,const double collapse_length_threshold,const CGAL_PMP_NP_CLASS & np)711 bool remove_almost_degenerate_faces(TriangleMesh& tmesh,
712                                     const double cap_threshold,
713                                     const double needle_threshold,
714                                     const double collapse_length_threshold,
715                                     const CGAL_PMP_NP_CLASS& np)
716 {
717   return remove_almost_degenerate_faces(faces(tmesh), tmesh, cap_threshold, needle_threshold,
718                                         collapse_length_threshold, np);
719 }
720 
721 template<class TriangleMesh>
remove_almost_degenerate_faces(TriangleMesh & tmesh,const double cap_threshold,const double needle_threshold,const double collapse_length_threshold)722 bool remove_almost_degenerate_faces(TriangleMesh& tmesh,
723                                     const double cap_threshold,
724                                     const double needle_threshold,
725                                     const double collapse_length_threshold)
726 {
727   return remove_almost_degenerate_faces(faces(tmesh), tmesh,
728                                         cap_threshold, needle_threshold, collapse_length_threshold,
729                                         CGAL::parameters::all_default());
730 }
731 
732 } // namespace experimental
733 
734 ////////////////////////////////////////////////////////////////////////////////////////////////////
735 ////////////////////////////////////////////////////////////////////////////////////////////////////
736 ////////////////////////////////////////////////////////////////////////////////////////////////////
737 ////////////////////////////////////////////////////////////////////////////////////////////////////
738 /// Remove degenerate_edges/faces
739 
740 namespace internal {
741 
742 template <typename HalfedgeGraph, typename VertexPointMap, typename Traits>
743 struct Less_vertex_point
744 {
745   typedef typename boost::graph_traits<HalfedgeGraph>::vertex_descriptor    vertex_descriptor;
746 
Less_vertex_pointLess_vertex_point747   Less_vertex_point(const Traits& traits, const VertexPointMap& vpmap)
748     : m_traits(traits), m_vpmap(vpmap)
749   {}
750 
operatorLess_vertex_point751   bool operator()(vertex_descriptor v1, vertex_descriptor v2) const {
752     return m_traits.less_xyz_3_object()(get(m_vpmap, v1), get(m_vpmap, v2));
753   }
754 
755   const Traits& m_traits;
756   const VertexPointMap& m_vpmap;
757 };
758 
759 } // end namespace internal
760 
761 // this function removes a border edge even if it does not satisfy the link condition.
762 // null_vertex() is returned if the removal changes the topology of the input
763 template <typename TriangleMesh, typename EdgeSet, typename FaceSet>
764 typename boost::graph_traits<TriangleMesh>::vertex_descriptor
remove_a_border_edge(typename boost::graph_traits<TriangleMesh>::edge_descriptor ed,TriangleMesh & tm,EdgeSet & input_range,EdgeSet & edge_set,FaceSet & face_set)765 remove_a_border_edge(typename boost::graph_traits<TriangleMesh>::edge_descriptor ed,
766                      TriangleMesh& tm,
767                      EdgeSet& input_range,
768                      EdgeSet& edge_set,
769                      FaceSet& face_set)
770 {
771   typedef boost::graph_traits<TriangleMesh>                             GT;
772   typedef typename GT::vertex_descriptor                                vertex_descriptor;
773   typedef typename GT::halfedge_descriptor                              halfedge_descriptor;
774   typedef typename GT::edge_descriptor                                  edge_descriptor;
775   typedef typename GT::face_descriptor                                  face_descriptor;
776 
777   halfedge_descriptor h = halfedge(ed, tm);
778 
779   if(is_border(h, tm))
780     h = opposite(h, tm);
781 
782   halfedge_descriptor opp_h = opposite(h, tm);
783   CGAL_assertion(is_border(opp_h, tm));
784   CGAL_assertion(!is_border(h, tm));
785 
786   CGAL_assertion(next(next(opp_h, tm), tm) != opp_h); // not working for a hole made of 2 edges
787   CGAL_assertion(next(next(next(opp_h, tm), tm), tm) != opp_h); // not working for a hole make of 3 edges
788 
789   if(CGAL::Euler::does_satisfy_link_condition(edge(h, tm), tm))
790   {
791     edge_set.erase(ed);
792     input_range.erase(ed);
793     halfedge_descriptor h = halfedge(ed, tm);
794     if(is_border(h, tm))
795       h = opposite(h, tm);
796 
797     const edge_descriptor prev_e = edge(prev(h, tm), tm);
798     edge_set.erase(prev_e);
799     input_range.erase(prev_e);
800     face_set.erase(face(h, tm));
801 
802     return CGAL::Euler::collapse_edge(ed, tm);
803   }
804 
805   // collect edges that have one vertex in the link of
806   // the vertices of h and one of the vertex of h as other vertex
807   std::set<edge_descriptor> common_incident_edges;
808   for(halfedge_descriptor hos : halfedges_around_source(h, tm))
809   {
810     for(halfedge_descriptor hot : halfedges_around_target(h, tm))
811     {
812       if(target(hos, tm) == source(hot, tm))
813       {
814         common_incident_edges.insert(edge(hot, tm));
815         common_incident_edges.insert(edge(hos, tm));
816       }
817     }
818   }
819 
820   CGAL_assertion(common_incident_edges.size() >= 2);
821 
822   // in the following loop, we visit define a connected component of
823   // faces bounded by edges in common_incident_edges and h. We look
824   // for the maximal one. This set of faces is the one that will
825   // disappear while collapsing ed
826   std::set<face_descriptor> marked_faces;
827 
828   std::vector<halfedge_descriptor> queue;
829   queue.push_back(opposite(next(h, tm), tm));
830   queue.push_back(opposite(prev(h, tm), tm));
831   marked_faces.insert(face(h, tm));
832 
833   do
834   {
835     std::vector<halfedge_descriptor> boundary;
836     while(!queue.empty())
837     {
838       halfedge_descriptor back=queue.back();
839       queue.pop_back();
840       face_descriptor fback=face(back, tm);
841       if(common_incident_edges.count(edge(back, tm)))
842       {
843         boundary.push_back(back);
844         continue;
845       }
846 
847       if(fback==GT::null_face() || !marked_faces.insert(fback).second)
848         continue;
849 
850       queue.push_back(opposite(next(back, tm), tm));
851       if(is_border(queue.back(), tm))
852       {
853 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
854         std::cout << "Boundary reached during exploration, the region to be removed is not a topological disk, not handled for now.\n";
855 #endif
856         return GT::null_vertex();
857       }
858 
859       queue.push_back(opposite(prev(back, tm), tm));
860       if(is_border(queue.back(), tm))
861       {
862 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
863         std::cout << "Boundary reached during exploration, the region to be removed is not a topological disk, not handled for now.\n";
864 #endif
865         return GT::null_vertex();
866       }
867     }
868 
869     CGAL_assertion(boundary.size() == 2);
870     common_incident_edges.erase(edge(boundary[0], tm));
871     common_incident_edges.erase(edge(boundary[1], tm));
872     if(!is_border(boundary[0], tm) || common_incident_edges.empty())
873       queue.push_back(boundary[0]);
874     if(!is_border(boundary[1], tm) || common_incident_edges.empty())
875       queue.push_back(boundary[1]);
876   }
877   while(!common_incident_edges.empty());
878 
879   // hk1 and hk2 are bounding the region that will be removed.
880   // The edge of hk2 will be removed and hk2 will be replaced
881   // by the opposite edge of hk1
882   halfedge_descriptor hk1 = queue.front();
883   halfedge_descriptor hk2 = queue.back();
884   if(target(hk1, tm)!=source(hk2, tm))
885     std::swap(hk1, hk2);
886 
887   CGAL_assertion(target(hk1, tm) == source(hk2, tm));
888   CGAL_assertion(source(hk1, tm) == source(h, tm));
889   CGAL_assertion(target(hk2, tm) == target(h, tm));
890 
891   CGAL_assertion(is_valid_polygon_mesh(tm));
892   if(!is_selection_a_topological_disk(marked_faces, tm))
893   {
894 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
895     std::cout << "The region to be removed is not a topological disk, not handled for now.\n";
896 #endif
897     return GT::null_vertex();
898   }
899 
900   if(is_border(hk1, tm) && is_border(hk2, tm))
901   {
902 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
903     std::cout << "The region to be removed is an isolated region, not handled for now.\n";
904 #endif
905     return GT::null_vertex();
906   }
907 
908   // collect vertices and edges to remove and do remove faces
909   std::set<edge_descriptor> edges_to_remove;
910   std::set<vertex_descriptor> vertices_to_remove;
911   for(face_descriptor fd : marked_faces)
912   {
913     halfedge_descriptor hd = halfedge(fd, tm);
914     for(int i=0; i<3; ++i)
915     {
916       edges_to_remove.insert(edge(hd, tm));
917       vertices_to_remove.insert(target(hd, tm));
918       hd = next(hd, tm);
919     }
920   }
921 
922   vertex_descriptor vkept = source(hk1, tm);
923 
924   //back-up next, prev halfedge pointers to be restored after removal
925   halfedge_descriptor hp = prev(opp_h, tm);
926   halfedge_descriptor hn = next(opp_h, tm);
927   halfedge_descriptor hk1_opp_next = next(hk2, tm);
928   halfedge_descriptor hk1_opp_prev = prev(hk2, tm);
929   face_descriptor hk1_opp_face = face(hk2, tm);
930 
931   // we will remove the target of hk2, update vertex pointers
932   for(halfedge_descriptor hot : halfedges_around_target(hk2, tm))
933     set_target(hot, vkept, tm);
934 
935   // update halfedge pointers since hk2 will be removed
936   set_halfedge(vkept, opposite(hk1, tm), tm);
937   set_halfedge(target(hk1, tm), hk1, tm);
938 
939   // do not remove hk1 and its vertices
940   vertices_to_remove.erase(vkept);
941   vertices_to_remove.erase(target(hk1, tm));
942   edges_to_remove.erase(edge(hk1, tm));
943 
944   bool hk2_equals_hp = (hk2 == hp);
945   CGAL_assertion(is_border(hk2, tm) == hk2_equals_hp);
946 
947   /*
948   - case hk2!=hp
949 
950          /\      /
951      hk1/  \hk2 /
952        /    \  /
953   ____/______\/____
954   hn   h_opp   hp
955 
956   - case hk2==hp
957 
958          /\
959      hk1/  \hk2 == hp
960        /    \
961   ____/______\
962   hn   h_opp
963   */
964 
965   // remove vertices
966   for(vertex_descriptor vd : vertices_to_remove)
967     remove_vertex(vd, tm);
968 
969   // remove edges
970   for(edge_descriptor ed : edges_to_remove)
971   {
972     edge_set.erase(ed);
973     input_range.erase(ed);
974     remove_edge(ed, tm);
975   }
976 
977   // remove faces
978   for(face_descriptor fd : marked_faces)
979   {
980     face_set.erase(fd);
981     remove_face(fd, tm);
982   }
983 
984   // now update pointers
985   set_face(opposite(hk1, tm), hk1_opp_face, tm);
986   if(!hk2_equals_hp)
987   {
988     set_next(hp, hn, tm);
989     set_next(opposite(hk1, tm), hk1_opp_next, tm);
990     set_next(hk1_opp_prev, opposite(hk1, tm), tm);
991     set_halfedge(hk1_opp_face, opposite(hk1, tm), tm);
992   }
993   else
994   {
995     set_next(hk1_opp_prev, opposite(hk1, tm), tm);
996     set_next(opposite(hk1, tm), hn, tm);
997   }
998 
999   CGAL_assertion(is_valid_polygon_mesh(tm));
1000   return vkept;
1001 }
1002 
1003 template <typename TriangleMesh>
1004 typename boost::graph_traits<TriangleMesh>::vertex_descriptor
remove_a_border_edge(typename boost::graph_traits<TriangleMesh>::edge_descriptor ed,TriangleMesh & tm)1005 remove_a_border_edge(typename boost::graph_traits<TriangleMesh>::edge_descriptor ed,
1006                      TriangleMesh& tm)
1007 {
1008   std::set<typename boost::graph_traits<TriangleMesh>::edge_descriptor> input_range;
1009   std::set<typename boost::graph_traits<TriangleMesh>::edge_descriptor> edge_set;
1010   std::set<typename boost::graph_traits<TriangleMesh>::face_descriptor> face_set;
1011 
1012   return remove_a_border_edge(ed, tm, input_range, edge_set, face_set);
1013 }
1014 
1015 template <typename EdgeRange, typename TriangleMesh, typename NamedParameters, typename FaceSet>
remove_degenerate_edges(const EdgeRange & edge_range,TriangleMesh & tmesh,FaceSet & face_set,const NamedParameters & np)1016 bool remove_degenerate_edges(const EdgeRange& edge_range,
1017                              TriangleMesh& tmesh,
1018                              FaceSet& face_set,
1019                              const NamedParameters& np)
1020 {
1021   CGAL_assertion(CGAL::is_triangle_mesh(tmesh));
1022   CGAL_assertion(CGAL::is_valid_polygon_mesh(tmesh));
1023 
1024   using parameters::get_parameter;
1025   using parameters::choose_parameter;
1026 
1027   typedef TriangleMesh                                                            TM;
1028   typedef typename boost::graph_traits<TriangleMesh>                              GT;
1029   typedef typename GT::vertex_descriptor                                          vertex_descriptor;
1030   typedef typename GT::halfedge_descriptor                                        halfedge_descriptor;
1031   typedef typename GT::edge_descriptor                                            edge_descriptor;
1032   typedef typename GT::face_descriptor                                            face_descriptor;
1033 
1034   typedef typename GetVertexPointMap<TM, NamedParameters>::type                   VertexPointMap;
1035   VertexPointMap vpmap = choose_parameter(get_parameter(np, internal_np::vertex_point),
1036                                           get_property_map(vertex_point, tmesh));
1037 
1038   typedef typename GetGeomTraits<TM, NamedParameters>::type                       Traits;
1039 
1040   std::size_t nb_deg_faces = 0;
1041   bool all_removed = false;
1042   bool some_removed = true;
1043   bool preserve_genus = choose_parameter(get_parameter(np, internal_np::preserve_genus), true);
1044 
1045   // The input edge range needs to be kept up-to-date
1046   std::set<edge_descriptor> local_edge_range(std::begin(edge_range), std::end(edge_range));
1047 
1048   // collect edges of length 0
1049   while(some_removed && !all_removed)
1050   {
1051     some_removed = false;
1052     all_removed = true;
1053     std::set<edge_descriptor> degenerate_edges_to_remove;
1054     degenerate_edges(local_edge_range, tmesh, std::inserter(degenerate_edges_to_remove,
1055                                                             degenerate_edges_to_remove.end()));
1056 
1057 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
1058     std::cout << "Found " << degenerate_edges_to_remove.size() << " null edges.\n";
1059 #endif
1060 
1061     // first try to remove all collapsable edges
1062     typename std::set<edge_descriptor>::iterator it = degenerate_edges_to_remove.begin();
1063     while(it != degenerate_edges_to_remove.end())
1064     {
1065       edge_descriptor e = *it;
1066       if(CGAL::Euler::does_satisfy_link_condition(e, tmesh))
1067       {
1068         const halfedge_descriptor h = halfedge(e, tmesh);
1069         local_edge_range.erase(*it);
1070         degenerate_edges_to_remove.erase(it);
1071 
1072         // remove edges that could also be set for removal
1073         if(face(h, tmesh) != GT::null_face())
1074         {
1075           ++nb_deg_faces;
1076           const edge_descriptor prev_e = edge(prev(h, tmesh), tmesh);
1077           degenerate_edges_to_remove.erase(prev_e);
1078           local_edge_range.erase(prev_e);
1079           face_set.erase(face(h, tmesh));
1080         }
1081 
1082         if(face(opposite(h, tmesh), tmesh) != GT::null_face())
1083         {
1084           ++nb_deg_faces;
1085           const edge_descriptor prev_opp_e = edge(prev(opposite(h, tmesh), tmesh), tmesh);
1086           degenerate_edges_to_remove.erase(prev_opp_e);
1087           local_edge_range.erase(prev_opp_e);
1088           face_set.erase(face(opposite(h, tmesh), tmesh));
1089         }
1090 
1091         //now remove the edge
1092         CGAL::Euler::collapse_edge(e, tmesh);
1093 
1094         // some_removed is not updated on purpose because if nothing
1095         //  happens below then nothing can be done
1096         it = degenerate_edges_to_remove.begin();
1097       }
1098       else // link condition not satisfied
1099       {
1100         ++it;
1101       }
1102     }
1103 
1104     CGAL_assertion(is_valid_polygon_mesh(tmesh));
1105 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
1106     std::cout << "Remaining " << degenerate_edges_to_remove.size() << " null edges to be handled.\n";
1107 #endif
1108 
1109     while(!degenerate_edges_to_remove.empty())
1110     {
1111       auto eb = degenerate_edges_to_remove.begin();
1112       const edge_descriptor ed = *eb;
1113       degenerate_edges_to_remove.erase(eb);
1114       local_edge_range.erase(ed);
1115 
1116       const halfedge_descriptor h = halfedge(ed, tmesh);
1117 
1118       if(CGAL::Euler::does_satisfy_link_condition(ed, tmesh))
1119       {
1120         // remove edges that could also be set for removal
1121         if(face(h, tmesh) != GT::null_face())
1122         {
1123           ++nb_deg_faces;
1124           const edge_descriptor prev_e = edge(prev(h, tmesh), tmesh);
1125           degenerate_edges_to_remove.erase(prev_e);
1126           local_edge_range.erase(prev_e);
1127           face_set.erase(face(h, tmesh));
1128         }
1129 
1130         if(face(opposite(h, tmesh), tmesh)!=GT::null_face())
1131         {
1132           ++nb_deg_faces;
1133           const edge_descriptor prev_opp_e = edge(prev(opposite(h, tmesh), tmesh), tmesh);
1134           degenerate_edges_to_remove.erase(prev_opp_e);
1135           local_edge_range.erase(prev_opp_e);
1136           face_set.erase(face(opposite(h, tmesh), tmesh));
1137         }
1138 
1139         //now remove the edge
1140         CGAL::Euler::collapse_edge(ed, tmesh);
1141         some_removed = true;
1142       }
1143       else // link condition not satisfied
1144       {
1145         // handle the case when the edge is incident to a triangle hole
1146         // we first fill the hole and try again
1147         if(is_border(ed, tmesh))
1148         {
1149           halfedge_descriptor hd = halfedge(ed, tmesh);
1150           if(!is_border(hd, tmesh))
1151             hd = opposite(hd, tmesh);
1152 
1153           if(is_triangle(hd, tmesh))
1154           {
1155             if(!preserve_genus)
1156             {
1157               Euler::fill_hole(hd, tmesh);
1158               degenerate_edges_to_remove.insert(ed); // reinsert the edge for future processing
1159               local_edge_range.insert(ed);
1160             }
1161             else
1162             {
1163               all_removed=false;
1164             }
1165 
1166             continue;
1167           }
1168 
1169 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
1170           std::cout << "Calling remove_a_border_edge\n";
1171 #endif
1172 
1173           vertex_descriptor vd = remove_a_border_edge(ed, tmesh, local_edge_range,
1174                                                       degenerate_edges_to_remove, face_set);
1175           if(vd == GT::null_vertex())
1176           {
1177             // @todo: if some border edges are later removed, the edge might be processable later
1178             // for example if it belongs to  boundary cycle of edges where the number of non-degenerate
1179             // edges is 2. That's what happen with fused_vertices.off in the testsuite where the edges
1180             // are not processed the same way with Polyhedron and Surface_mesh. In the case of Polyhedron
1181             // more degenerate edges could be removed.
1182             all_removed = false;
1183           }
1184           else
1185             some_removed = true;
1186 
1187           continue;
1188         }
1189         else
1190         {
1191           halfedge_descriptor hd = halfedge(ed, tmesh);
1192           // if both vertices are boundary vertices we can't do anything
1193           bool impossible = false;
1194           for(halfedge_descriptor h : halfedges_around_target(hd, tmesh))
1195           {
1196             if(is_border(h, tmesh))
1197             {
1198               impossible = true;
1199               break;
1200             }
1201           }
1202 
1203           if(impossible)
1204           {
1205             impossible = false;
1206             for(halfedge_descriptor h : halfedges_around_source(hd, tmesh))
1207             {
1208               if(is_border(h, tmesh))
1209               {
1210                 impossible = true;
1211                 break;
1212               }
1213             }
1214 
1215             if(impossible)
1216             {
1217               all_removed = false;
1218               continue;
1219             }
1220           }
1221         }
1222 
1223         // When the edge does not satisfy the link condition, it means that it cannot be
1224         // collapsed as is. In the following if there is a topological issue
1225         // with contracting the edge (component or geometric feature that disappears),
1226         // nothing is done.
1227         // We start by marking the faces that are incident to an edge endpoint.
1228         // If the set of marked faces is a topologically disk, then we simply remove all the simplicies
1229         // inside the disk and star the hole with the edge vertex kept.
1230         // If the set of marked faces is not a topological disk, it has some non-manifold vertices
1231         // on its boundary. We need to mark additional faces to make it a topological disk.
1232         // We can then apply the star hole procedure.
1233         // Right now we additionally mark the smallest connected components of non-marked faces
1234         // (using the number of faces)
1235 
1236         //backup central point
1237         typename Traits::Point_3 pt = get(vpmap, source(ed, tmesh));
1238 
1239         // mark faces of the link of each endpoints of the edge which collapse is not topologically valid
1240         std::set<face_descriptor> marked_faces;
1241 
1242         //   first endpoint
1243         for(halfedge_descriptor hd : CGAL::halfedges_around_target(halfedge(ed, tmesh), tmesh))
1244           if(!is_border(hd, tmesh))
1245             marked_faces.insert(face(hd, tmesh));
1246 
1247         //   second endpoint
1248         for(halfedge_descriptor hd : CGAL::halfedges_around_target(opposite(halfedge(ed, tmesh), tmesh), tmesh))
1249           if(!is_border(hd, tmesh))
1250             marked_faces.insert(face(hd, tmesh));
1251 
1252         // extract the halfedges on the boundary of the marked region
1253         std::vector<halfedge_descriptor> border;
1254         for(face_descriptor fd : marked_faces)
1255         {
1256           for(halfedge_descriptor hd : CGAL::halfedges_around_face(halfedge(fd, tmesh), tmesh))
1257           {
1258             halfedge_descriptor hd_opp = opposite(hd, tmesh);
1259             if(is_border(hd_opp, tmesh) || marked_faces.count(face(hd_opp, tmesh)) == 0)
1260             {
1261               border.push_back(hd);
1262             }
1263           }
1264         }
1265 
1266         if(border.empty())
1267         {
1268           // a whole connected component (without boundary) got selected and will disappear (not handled for now)
1269 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
1270           std::cout << "Trying to remove a whole connected component, not handled yet\n";
1271 #endif
1272           all_removed = false;
1273           continue;
1274         }
1275 
1276         // define cc of border halfedges: two halfedges are in the same cc
1277         // if they are on the border of the cc of non-marked faces.
1278         typedef CGAL::Union_find<halfedge_descriptor>                     UF_ds;
1279         UF_ds uf;
1280         std::map<halfedge_descriptor, typename UF_ds::handle> handles;
1281 
1282         // one cc per border halfedge
1283         for(halfedge_descriptor hd : border)
1284           handles.insert(std::make_pair(hd, uf.make_set(hd)));
1285 
1286         // join cc's
1287         for(halfedge_descriptor hd : border)
1288         {
1289           CGAL_assertion(marked_faces.count(face(hd, tmesh)) > 0);
1290           CGAL_assertion(marked_faces.count(face(opposite(hd, tmesh), tmesh)) == 0);
1291           halfedge_descriptor candidate = hd;
1292 
1293           do
1294           {
1295             candidate = prev(opposite(candidate, tmesh), tmesh);
1296           }
1297           while(!marked_faces.count(face(opposite(candidate, tmesh), tmesh)));
1298 
1299           uf.unify_sets(handles[hd], handles[opposite(candidate, tmesh)]);
1300         }
1301 
1302         std::size_t nb_cc = uf.number_of_sets();
1303         if(nb_cc != 1)
1304         {
1305           // if more than one connected component is found then the patch
1306           // made of marked faces contains "non-manifold" vertices.
1307           // The smallest components need to be marked so that the patch
1308           // made of marked faces is a topological disk
1309 
1310           // we will explore in parallel the connected components and will stop
1311           // when all but one connected component have been entirely explored.
1312           // We add one face at a time for each cc in order to not explore a
1313           // potentially very large cc.
1314           std::vector<std::vector<halfedge_descriptor> > stacks_per_cc(nb_cc);
1315           std::vector<std::set<face_descriptor> > faces_per_cc(nb_cc);
1316           std::vector<bool> exploration_finished(nb_cc, false);
1317 
1318           // init the stacks of halfedges using the cc of the boundary
1319           std::size_t index = 0;
1320           std::map<halfedge_descriptor, std::size_t > ccs;
1321 
1322           typedef std::pair<const halfedge_descriptor, typename UF_ds::handle> Pair_type;
1323           for(const Pair_type& p : handles)
1324           {
1325             halfedge_descriptor opp_hedge = opposite(p.first, tmesh);
1326             if(is_border(opp_hedge, tmesh))
1327               continue; // nothing to do on the boundary
1328 
1329             typedef typename std::map<halfedge_descriptor, std::size_t >::iterator Map_it;
1330             std::pair<Map_it, bool> insert_res = ccs.insert(std::make_pair(*uf.find(p.second), index));
1331 
1332             if(insert_res.second)
1333               ++index;
1334 
1335             stacks_per_cc[insert_res.first->second].push_back(prev(opp_hedge, tmesh));
1336             stacks_per_cc[insert_res.first->second].push_back(next(opp_hedge, tmesh));
1337             faces_per_cc[insert_res.first->second].insert(face(opp_hedge, tmesh));
1338           }
1339 
1340           if(index != nb_cc)
1341           {
1342             // most probably, one cc is a cycle of border edges
1343 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
1344             std::cout << "Trying to remove a component with a cycle of halfedges (nested hole or whole component), not handled yet.\n";
1345 #endif
1346             all_removed = false;
1347             continue;
1348           }
1349 
1350           std::size_t nb_ccs_to_be_explored = nb_cc;
1351           index = 0;
1352           //explore the cc's
1353           do
1354           {
1355             // try to extract one more face for a given cc
1356             do
1357             {
1358               CGAL_assertion(!exploration_finished[index]);
1359               CGAL_assertion(!stacks_per_cc.empty());
1360               CGAL_assertion(!stacks_per_cc[index].empty());
1361 
1362               halfedge_descriptor hd = stacks_per_cc[index].back();
1363               stacks_per_cc[index].pop_back();
1364               hd = opposite(hd, tmesh);
1365 
1366               if(!is_border(hd, tmesh) && !marked_faces.count(face(hd, tmesh)))
1367               {
1368                 if(faces_per_cc[index].insert(face(hd, tmesh)).second)
1369                 {
1370                   stacks_per_cc[index].push_back(next(hd, tmesh));
1371                   stacks_per_cc[index].push_back(prev(hd, tmesh));
1372                   break;
1373                 }
1374               }
1375 
1376               if(stacks_per_cc[index].empty())
1377                 break;
1378             }
1379             while(true);
1380 
1381             // the exploration of a cc is finished when its stack is empty
1382             exploration_finished[index]=stacks_per_cc[index].empty();
1383             if(exploration_finished[index])
1384               --nb_ccs_to_be_explored;
1385 
1386             if(nb_ccs_to_be_explored==1)
1387               break;
1388 
1389             while(exploration_finished[(++index)%nb_cc]);
1390 
1391             index = index%nb_cc;
1392           }
1393           while(true);
1394 
1395           // @todo use the area criteria? this means maybe continue exploration of larger cc
1396           // mark faces of completetly explored cc
1397           for(index=0; index<nb_cc; ++index)
1398           {
1399             if(exploration_finished[index])
1400             {
1401               for(face_descriptor fd : faces_per_cc[index])
1402                 marked_faces.insert(fd);
1403             }
1404           }
1405         }
1406 
1407         // make sure the selection is a topological disk (otherwise we need another treatment)
1408         if(!is_selection_a_topological_disk(marked_faces, tmesh))
1409         {
1410 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
1411           std::cout << "Trying to handle a non-topological disk, do nothing\n";
1412 #endif
1413           all_removed = false;
1414           continue;
1415         }
1416 
1417         some_removed = true;
1418 
1419         // collect simplices to be removed
1420         std::set<vertex_descriptor> vertices_to_keep;
1421         std::set<halfedge_descriptor> halfedges_to_keep;
1422         for(halfedge_descriptor hd : border)
1423         {
1424           if(!marked_faces.count(face(opposite(hd, tmesh), tmesh)))
1425           {
1426             halfedges_to_keep.insert(hd);
1427             vertices_to_keep.insert(target(hd, tmesh));
1428           }
1429         }
1430 
1431         // backup next,prev relationships to set after patch removal
1432         std::vector<std::pair<halfedge_descriptor, halfedge_descriptor> > next_prev_halfedge_pairs;
1433         halfedge_descriptor first_border_hd = *(halfedges_to_keep.begin());
1434         halfedge_descriptor current_border_hd = first_border_hd;
1435         do
1436         {
1437           halfedge_descriptor prev_border_hd = current_border_hd;
1438           current_border_hd = next(current_border_hd, tmesh);
1439 
1440           while(marked_faces.count(face(opposite(current_border_hd, tmesh), tmesh)))
1441             current_border_hd=next(opposite(current_border_hd, tmesh), tmesh);
1442 
1443           next_prev_halfedge_pairs.push_back(std::make_pair(prev_border_hd, current_border_hd));
1444         }
1445         while(current_border_hd!=first_border_hd);
1446 
1447         // collect vertices and edges to remove and do remove faces
1448         std::set<edge_descriptor> edges_to_remove;
1449         std::set<vertex_descriptor> vertices_to_remove;
1450         for(face_descriptor fd : marked_faces)
1451         {
1452           halfedge_descriptor hd=halfedge(fd, tmesh);
1453           for(int i=0; i<3; ++i)
1454           {
1455             if(!halfedges_to_keep.count(hd))
1456               edges_to_remove.insert(edge(hd, tmesh));
1457 
1458             if(!vertices_to_keep.count(target(hd, tmesh)))
1459               vertices_to_remove.insert(target(hd, tmesh));
1460 
1461             hd = next(hd, tmesh);
1462           }
1463 
1464           remove_face(fd, tmesh);
1465           face_set.erase(fd);
1466         }
1467 
1468         // remove vertices
1469         for(vertex_descriptor vd : vertices_to_remove)
1470           remove_vertex(vd, tmesh);
1471 
1472         // remove edges
1473         for(edge_descriptor ed : edges_to_remove)
1474         {
1475           degenerate_edges_to_remove.erase(ed);
1476           local_edge_range.erase(ed);
1477           remove_edge(ed, tmesh);
1478         }
1479 
1480         // add a new face, set all border edges pointing to it
1481         // and update halfedge vertex of patch boundary vertices
1482         face_descriptor new_face = add_face(tmesh);
1483         typedef std::pair<halfedge_descriptor, halfedge_descriptor> Pair_type;
1484         for(const Pair_type& p : next_prev_halfedge_pairs)
1485         {
1486           set_face(p.first, new_face, tmesh);
1487           set_next(p.first, p.second, tmesh);
1488           set_halfedge(target(p.first, tmesh), p.first, tmesh);
1489         }
1490 
1491         set_halfedge(new_face, first_border_hd, tmesh);
1492 
1493         // triangulate the new face and update the coordinate of the central vertex
1494         halfedge_descriptor new_hd=Euler::add_center_vertex(first_border_hd, tmesh);
1495         put(vpmap, target(new_hd, tmesh), pt);
1496 
1497         for(halfedge_descriptor hd : halfedges_around_target(new_hd, tmesh))
1498         {
1499           const edge_descriptor inc_e = edge(hd, tmesh);
1500           if(is_degenerate_edge(inc_e, tmesh, np))
1501           {
1502             degenerate_edges_to_remove.insert(inc_e);
1503             local_edge_range.insert(inc_e);
1504           }
1505 
1506           if(face(hd, tmesh) != GT::null_face() && is_degenerate_triangle_face(face(hd, tmesh), tmesh))
1507             face_set.insert(face(hd, tmesh));
1508         }
1509 
1510         CGAL_expensive_assertion(is_valid_polygon_mesh(tmesh));
1511       }
1512     }
1513   }
1514 
1515   return all_removed;
1516 }
1517 
1518 template <typename EdgeRange, typename TriangleMesh, typename CGAL_PMP_NP_TEMPLATE_PARAMETERS>
remove_degenerate_edges(const EdgeRange & edge_range,TriangleMesh & tmesh,const CGAL_PMP_NP_CLASS & np)1519 bool remove_degenerate_edges(const EdgeRange& edge_range,
1520                              TriangleMesh& tmesh,
1521                              const CGAL_PMP_NP_CLASS& np)
1522 {
1523   std::set<typename boost::graph_traits<TriangleMesh>::face_descriptor> face_set;
1524   return remove_degenerate_edges(edge_range, tmesh, face_set, np);
1525 }
1526 
1527 template <typename TriangleMesh, typename CGAL_PMP_NP_TEMPLATE_PARAMETERS>
remove_degenerate_edges(TriangleMesh & tmesh,const CGAL_PMP_NP_CLASS & np)1528 bool remove_degenerate_edges(TriangleMesh& tmesh,
1529                              const CGAL_PMP_NP_CLASS& np)
1530 {
1531   std::set<typename boost::graph_traits<TriangleMesh>::face_descriptor> face_set;
1532   return remove_degenerate_edges(edges(tmesh), tmesh, face_set, np);
1533 }
1534 
1535 template <typename EdgeRange, typename TriangleMesh>
remove_degenerate_edges(const EdgeRange & edge_range,TriangleMesh & tmesh)1536 bool remove_degenerate_edges(const EdgeRange& edge_range,
1537                              TriangleMesh& tmesh)
1538 {
1539   std::set<typename boost::graph_traits<TriangleMesh>::face_descriptor> face_set;
1540   return remove_degenerate_edges(edge_range, tmesh, face_set, parameters::all_default());
1541 }
1542 
1543 template <typename TriangleMesh>
remove_degenerate_edges(TriangleMesh & tmesh)1544 bool remove_degenerate_edges(TriangleMesh& tmesh)
1545 {
1546   std::set<typename boost::graph_traits<TriangleMesh>::face_descriptor> face_set;
1547   return remove_degenerate_edges(edges(tmesh), tmesh, face_set, parameters::all_default());
1548 }
1549 
1550 // \ingroup PMP_repairing_grp
1551 // removes the degenerate faces from a triangulated surface mesh.
1552 // A face is considered degenerate if two of its vertices share the same location,
1553 // or more generally if all its vertices are collinear.
1554 //
1555 // @pre `CGAL::is_triangle_mesh(tmesh)`
1556 //
1557 // @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph`
1558 // @tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters"
1559 //
1560 // @param tmesh the  triangulated surface mesh to be repaired
1561 // @param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below
1562 //
1563 // \cgalNamedParamsBegin
1564 //   \cgalParamNBegin{vertex_point_map}
1565 //     \cgalParamDescription{a property map associating points to the vertices of `tmesh`}
1566 //     \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits<TriangleMesh>::%vertex_descriptor`
1567 //                    as key type and `%Point_3` as value type}
1568 //     \cgalParamDefault{`boost::get(CGAL::vertex_point, tmesh)`}
1569 //     \cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t`
1570 //                     must be available in `TriangleMesh`.}
1571 //   \cgalParamNEnd
1572 //
1573 //   \cgalParamNBegin{geom_traits}
1574 //     \cgalParamDescription{an instance of a geometric traits class}
1575 //     \cgalParamType{The traits class must provide the nested type `Point_3`,
1576 //                    and the nested functors:
1577 //                    - `Compare_distance_3` to compute the distance between 2 points
1578 //                    - `Collinear_3` to check whether 3 points are collinear
1579 //                    - `Less_xyz_3` to compare lexicographically two points
1580 //                    - `Equal_3` to check whether 2 points are identical.
1581 //                    For each functor Foo, a function `Foo foo_object()` must be provided.}
1582 //     \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`}
1583 //     \cgalParamExtra{The geometric traits class must be compatible with the vertex point type.}
1584 //   \cgalParamNEnd
1585 // \cgalNamedParamsEnd
1586 //
1587 // \todo the function might not be able to remove all degenerate faces.
1588 //       We should probably do something with the return type.
1589 //
1590 // \return `true` if all degenerate faces were successfully removed, and `false` otherwise.
1591 template <typename FaceRange, typename TriangleMesh, typename NamedParameters>
remove_degenerate_faces(const FaceRange & face_range,TriangleMesh & tmesh,const NamedParameters & np)1592 bool remove_degenerate_faces(const FaceRange& face_range,
1593                              TriangleMesh& tmesh,
1594                              const NamedParameters& np)
1595 {
1596   CGAL_assertion(CGAL::is_triangle_mesh(tmesh));
1597   CGAL_assertion(CGAL::is_valid_polygon_mesh(tmesh));
1598 
1599   using parameters::get_parameter;
1600   using parameters::choose_parameter;
1601 
1602   typedef TriangleMesh                                                              TM;
1603   typedef typename boost::graph_traits<TriangleMesh>                                GT;
1604   typedef typename GT::vertex_descriptor                                            vertex_descriptor;
1605   typedef typename GT::halfedge_descriptor                                          halfedge_descriptor;
1606   typedef typename GT::edge_descriptor                                              edge_descriptor;
1607   typedef typename GT::face_descriptor                                              face_descriptor;
1608 
1609   typedef typename GetVertexPointMap<TM, NamedParameters>::type                     VertexPointMap;
1610   VertexPointMap vpmap = choose_parameter(get_parameter(np, internal_np::vertex_point),
1611                                           get_property_map(vertex_point, tmesh));
1612   typedef typename GetGeomTraits<TM, NamedParameters>::type                         Traits;
1613   Traits traits = choose_parameter(get_parameter(np, internal_np::geom_traits), Traits());
1614 
1615   typedef typename boost::property_traits<VertexPointMap>::value_type               Point_3;
1616   typedef typename boost::property_traits<VertexPointMap>::reference                Point_ref;
1617 
1618   std::set<face_descriptor> degenerate_face_set;
1619   degenerate_faces(face_range, tmesh, std::inserter(degenerate_face_set, degenerate_face_set.begin()), np);
1620 
1621   const std::size_t faces_size = faces(tmesh).size();
1622 
1623   if(degenerate_face_set.empty())
1624     return true;
1625 
1626   if(degenerate_face_set.size() == faces_size)
1627   {
1628     clear(tmesh);
1629     return true;
1630   }
1631 
1632   // Sanitize the face range by adding adjacent degenerate faces
1633   const std::size_t range_size = face_range.size();
1634   bool is_range_full_mesh = (range_size == faces_size);
1635   if(!is_range_full_mesh)
1636   {
1637     std::list<face_descriptor> faces_to_visit(degenerate_face_set.begin(), degenerate_face_set.end());
1638 
1639     while(!faces_to_visit.empty())
1640     {
1641       face_descriptor fd = faces_to_visit.front();
1642       faces_to_visit.pop_front();
1643 
1644       for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, tmesh), tmesh))
1645       {
1646         for(halfedge_descriptor inc_hd : halfedges_around_target(hd, tmesh))
1647         {
1648           face_descriptor adj_fd = face(inc_hd, tmesh);
1649           if(adj_fd == GT::null_face() || adj_fd == fd)
1650             continue;
1651 
1652           if(is_degenerate_triangle_face(adj_fd, tmesh))
1653           {
1654             if(degenerate_face_set.insert(adj_fd).second)
1655             {
1656               // successful insertion means we did not know about this face before
1657               faces_to_visit.push_back(adj_fd);
1658             }
1659           }
1660         }
1661       }
1662     }
1663   }
1664 
1665   // Note that there can't be any null edge incident to the degenerate faces range,
1666   // otherwise we would have a null face incident to the face range, and that is not possible
1667   // after the sanitization above
1668   std::set<edge_descriptor> edge_range;
1669   for(face_descriptor fd : degenerate_face_set)
1670     for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, tmesh), tmesh))
1671       edge_range.insert(edge(hd, tmesh));
1672 
1673   // First remove edges of length 0
1674   bool all_removed = remove_degenerate_edges(edge_range, tmesh, degenerate_face_set, np);
1675 
1676   CGAL_assertion_code(for(face_descriptor fd : degenerate_face_set) {)
1677   CGAL_assertion(is_degenerate_triangle_face(fd, tmesh));
1678   CGAL_assertion_code(})
1679 
1680 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
1681   {
1682     std::cout <<"Done with null edges.\n";
1683     CGAL::IO::write_polygon_mesh("/tmp/no_null_edges.off", tmesh, CGAL::parameters::stream_precision(17));
1684   }
1685 #endif
1686 
1687   // Then, remove triangles made of 3 collinear points
1688 
1689   // start by filtering out border faces
1690   // @todo shall we avoid doing that in case a non-manifold vertex on the boundary or if a whole component disappear?
1691   std::set<face_descriptor> border_deg_faces;
1692   for(face_descriptor f : degenerate_face_set)
1693   {
1694     halfedge_descriptor h = halfedge(f, tmesh);
1695     for(int i=0; i<3; ++i)
1696     {
1697       if(is_border(opposite(h, tmesh), tmesh))
1698       {
1699         border_deg_faces.insert(f);
1700         break;
1701       }
1702 
1703       h = next(h, tmesh);
1704     }
1705   }
1706 
1707   while(!border_deg_faces.empty())
1708   {
1709     face_descriptor f_to_rm = *border_deg_faces.begin();
1710     border_deg_faces.erase(border_deg_faces.begin());
1711 
1712     halfedge_descriptor h = halfedge(f_to_rm, tmesh);
1713     for(int i=0; i<3; ++i)
1714     {
1715       face_descriptor f = face(opposite(h, tmesh), tmesh);
1716       if(f!=GT::null_face())
1717       {
1718         if(is_degenerate_triangle_face(f, tmesh, np))
1719           border_deg_faces.insert(f);
1720       }
1721 
1722       h = next(h, tmesh);
1723     }
1724 
1725     while(!is_border(opposite(h, tmesh), tmesh))
1726     {
1727       h = next(h, tmesh);
1728     }
1729 
1730     degenerate_face_set.erase(f_to_rm);
1731     Euler::remove_face(h, tmesh);
1732   }
1733 
1734   // Ignore faces with null edges
1735   if(!all_removed)
1736   {
1737     typename std::set<face_descriptor>::iterator it = degenerate_face_set.begin();
1738     while(it != degenerate_face_set.end())
1739     {
1740       bool has_degenerate_edge = false;
1741       for(halfedge_descriptor hd : halfedges_around_face(halfedge(*it, tmesh), tmesh))
1742       {
1743         const edge_descriptor ed = edge(hd, tmesh);
1744         if(is_degenerate_edge(ed, tmesh, np))
1745         {
1746           has_degenerate_edge = true;
1747           it = degenerate_face_set.erase(it);
1748           break;
1749         }
1750       }
1751 
1752       if(!has_degenerate_edge)
1753         ++it;
1754     }
1755   }
1756 
1757   // first remove degree 3 vertices that are part of a cap
1758   // (only the vertex in the middle of the opposite edge)
1759   // This removal does not change the shape of the mesh.
1760   while(!degenerate_face_set.empty())
1761   {
1762     std::set<vertex_descriptor> vertices_to_remove;
1763     for(face_descriptor fd : degenerate_face_set)
1764     {
1765       for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, tmesh), tmesh))
1766       {
1767         vertex_descriptor vd = target(hd, tmesh);
1768         if(degree(vd, tmesh) == 3 && is_border(vd, tmesh)==GT::null_halfedge())
1769         {
1770           vertices_to_remove.insert(vd);
1771           break;
1772         }
1773       }
1774     }
1775 
1776     for(vertex_descriptor vd : vertices_to_remove)
1777     {
1778       for(halfedge_descriptor hd2 : halfedges_around_target(vd, tmesh))
1779         degenerate_face_set.erase(face(hd2, tmesh));
1780 
1781       // remove the central vertex and check if the new face is degenerated
1782       halfedge_descriptor hd = CGAL::Euler::remove_center_vertex(halfedge(vd, tmesh), tmesh);
1783       if(is_degenerate_triangle_face(face(hd, tmesh), tmesh, np))
1784       {
1785         degenerate_face_set.insert(face(hd, tmesh));
1786       }
1787     }
1788 
1789     if(vertices_to_remove.empty())
1790       break;
1791   }
1792 
1793   while(!degenerate_face_set.empty())
1794   {
1795 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
1796     std::cout << "Loop on removing deg faces\n";
1797 
1798     // ensure the mesh is not broken
1799     {
1800       std::ofstream out("/tmp/out.off");
1801       out << tmesh;
1802       out.close();
1803 
1804       std::vector<typename Traits::Point_3> points;
1805       std::vector<std::vector<std::size_t> > triangles;
1806       std::ifstream in("/tmp/out.off");
1807       CGAL::IO::read_OFF(in, points, triangles);
1808       if(!CGAL::Polygon_mesh_processing::is_polygon_soup_a_polygon_mesh(triangles))
1809       {
1810         std::cerr << "Warning: got a polygon soup (may simply be a non-manifold vertex)!\n";
1811       }
1812     }
1813 #endif
1814 
1815     face_descriptor fd = *degenerate_face_set.begin();
1816 
1817     // look whether an incident triangle is also degenerate
1818     bool detect_cc_of_degenerate_triangles = false;
1819     for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, tmesh), tmesh))
1820     {
1821       face_descriptor adjacent_face = face(opposite(hd, tmesh), tmesh);
1822       if(adjacent_face!=GT::null_face() && degenerate_face_set.count(adjacent_face))
1823       {
1824         detect_cc_of_degenerate_triangles = true;
1825         break;
1826       }
1827     }
1828 
1829     if(!detect_cc_of_degenerate_triangles)
1830     {
1831 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
1832       std::cout << "  no degenerate neighbors, using a flip.\n";
1833 #endif
1834       degenerate_face_set.erase(degenerate_face_set.begin());
1835 
1836       // flip the longest edge of the triangle
1837       Point_ref p1 = get(vpmap, target(halfedge(fd, tmesh), tmesh));
1838       Point_ref p2 = get(vpmap, target(next(halfedge(fd, tmesh), tmesh), tmesh));
1839       Point_ref p3 = get(vpmap, source(halfedge(fd, tmesh), tmesh));
1840 
1841       CGAL_assertion(p1!=p2 && p1!=p3 && p2!=p3);
1842 
1843       typename Traits::Compare_distance_3 compare_distance = traits.compare_distance_3_object();
1844 
1845       halfedge_descriptor edge_to_flip;
1846       if(compare_distance(p1,p2, p1,p3) != CGAL::SMALLER) // p1p2 > p1p3
1847       {
1848         if(compare_distance(p1,p2, p2,p3) != CGAL::SMALLER) // p1p2 > p2p3
1849           // flip p1p2
1850           edge_to_flip = next(halfedge(fd, tmesh), tmesh);
1851         else
1852           // flip p2p3
1853           edge_to_flip = prev(halfedge(fd, tmesh), tmesh);
1854       }
1855       else
1856       {
1857         if(compare_distance(p1,p3, p2,p3) != CGAL::SMALLER) // p1p3>p2p3
1858           //flip p3p1
1859           edge_to_flip = halfedge(fd, tmesh);
1860         else
1861           //flip p2p3
1862           edge_to_flip = prev(halfedge(fd, tmesh), tmesh);
1863       }
1864 
1865       face_descriptor opposite_face=face(opposite(edge_to_flip, tmesh), tmesh);
1866       if(opposite_face == GT::null_face())
1867       {
1868         // simply remove the face
1869         Euler::remove_face(edge_to_flip, tmesh);
1870       }
1871       else
1872       {
1873         // condition for the flip to be valid (the edge to be created do not already exists)
1874         if(!halfedge(target(next(edge_to_flip, tmesh), tmesh),
1875                        target(next(opposite(edge_to_flip, tmesh), tmesh), tmesh),
1876                        tmesh).second)
1877         {
1878           Euler::flip_edge(edge_to_flip, tmesh);
1879         }
1880         else
1881         {
1882           all_removed = false;
1883 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
1884           std::cout << "  WARNING: flip is not possible\n";
1885           // @todo Let p and q be the vertices opposite to `edge_to_flip`, and let
1886           //       r be the vertex of `edge_to_flip` that is the furthest away from
1887           //       the edge `pq`. In that case I think we should remove all the triangles
1888           //       so that the triangle pqr is in the mesh.
1889 #endif
1890         }
1891       }
1892     }
1893     else
1894     {
1895       // Process a connected component of degenerate faces
1896       // get all the faces from the connected component
1897       // and the boundary edges
1898       std::set<face_descriptor> cc_faces;
1899       std::vector<face_descriptor> queue;
1900       std::vector<halfedge_descriptor> boundary_hedges;
1901       std::vector<halfedge_descriptor> inside_hedges;
1902       queue.push_back(fd);
1903       cc_faces.insert(fd);
1904 
1905       while(!queue.empty())
1906       {
1907         face_descriptor top=queue.back();
1908         queue.pop_back();
1909         for(halfedge_descriptor hd : halfedges_around_face(halfedge(top, tmesh), tmesh))
1910         {
1911           face_descriptor adjacent_face = face(opposite(hd, tmesh), tmesh);
1912           if(adjacent_face==GT::null_face() || degenerate_face_set.count(adjacent_face)==0)
1913           {
1914             boundary_hedges.push_back(hd);
1915           }
1916           else
1917           {
1918             if(cc_faces.insert(adjacent_face).second)
1919               queue.push_back(adjacent_face);
1920 
1921             if(hd < opposite(hd, tmesh))
1922               inside_hedges.push_back(hd);
1923           }
1924         }
1925       }
1926 
1927 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
1928       std::cout << "  Deal with a cc of " << cc_faces.size() << " degenerate faces.\n";
1929       /// dump cc_faces
1930       {
1931         int id = 0;
1932         std::map<vertex_descriptor, int> vids;
1933         for(face_descriptor f : cc_faces)
1934         {
1935           if(vids.insert(std::make_pair(target(halfedge(f, tmesh), tmesh), id)).second) ++id;
1936           if(vids.insert(std::make_pair(target(next(halfedge(f, tmesh), tmesh), tmesh), id)).second) ++id;
1937           if(vids.insert(std::make_pair(target(next(next(halfedge(f, tmesh), tmesh), tmesh), tmesh), id)).second) ++id;
1938         }
1939 
1940         std::ofstream output("/tmp/cc_faces.off");
1941         output << std::setprecision(17);
1942         output << "OFF\n" << vids.size() << " " << cc_faces.size() << " 0\n";
1943         std::vector<typename Traits::Point_3> points(vids.size());
1944         typedef std::pair<const vertex_descriptor, int> Pair_type;
1945         for(const Pair_type& p : vids)
1946 
1947           points[p.second] = get(vpmap, p.first);
1948         for(const Point_3& p : points)
1949           output << p << "\n";
1950 
1951         for(face_descriptor f : cc_faces)
1952         {
1953           output << "3 "
1954                  << vids[target(halfedge(f, tmesh), tmesh)] << " "
1955                  << vids[target(next(halfedge(f, tmesh), tmesh), tmesh)] << " "
1956                  << vids[target(next(next(halfedge(f, tmesh), tmesh), tmesh), tmesh)] << "\n";
1957         }
1958 
1959         for(std::size_t pid=2; pid!=points.size(); ++pid)
1960         {
1961           CGAL_assertion(collinear(points[0], points[1], points[pid]));
1962         }
1963       }
1964 #endif
1965 
1966       // find vertices strictly inside the cc
1967       std::set<vertex_descriptor> boundary_vertices;
1968       for(halfedge_descriptor hd : boundary_hedges)
1969         boundary_vertices.insert(target(hd, tmesh));
1970 
1971       std::set<vertex_descriptor> inside_vertices;
1972       for(halfedge_descriptor hd : inside_hedges)
1973       {
1974         if(!boundary_vertices.count(target(hd, tmesh)))
1975           inside_vertices.insert(target(hd, tmesh));
1976         if(!boundary_vertices.count(source(hd, tmesh)))
1977           inside_vertices.insert(source(hd, tmesh));
1978       }
1979 
1980       // v-e+f = 1 for a topological disk and e = (3f+#boundary_edges)/2
1981       if(boundary_vertices.size()+inside_vertices.size() -
1982           (cc_faces.size()+boundary_hedges.size())/2 != 1)
1983       {
1984         //cc_faces does not define a topological disk
1985         // @todo Find to way to handle that case
1986 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
1987         std::cout << "  WARNING: Cannot remove the component of degenerate faces: not a topological disk.\n";
1988 #endif
1989 
1990         for(face_descriptor f : cc_faces)
1991           degenerate_face_set.erase(f);
1992 
1993         continue;
1994       }
1995 
1996       // preliminary step to check if the operation is possible
1997       // sort the boundary points along the common supporting line
1998       //    we first need a reference point
1999       typedef internal::Less_vertex_point<TriangleMesh, VertexPointMap, Traits> Less_vertex;
2000       std::pair<
2001           typename std::set<vertex_descriptor>::iterator,
2002           typename std::set<vertex_descriptor>::iterator > ref_vertices =
2003           boost::minmax_element(boundary_vertices.begin(),
2004                                 boundary_vertices.end(),
2005                                 Less_vertex(traits, vpmap));
2006 
2007       //    and then we sort the vertices using this reference point
2008       typedef std::set<Point_3>                                Sorted_point_set;
2009       Sorted_point_set sorted_points;
2010       for(vertex_descriptor v : boundary_vertices)
2011         sorted_points.insert(get(vpmap, v));
2012 
2013       CGAL_assertion(sorted_points.size()==
2014                      std::set<typename Traits::Point_3>(sorted_points.begin(),
2015                                                         sorted_points.end()).size());
2016 
2017       CGAL_assertion(get(vpmap, *ref_vertices.first) == *sorted_points.begin());
2018       CGAL_assertion(get(vpmap, *ref_vertices.second) == *std::prev(sorted_points.end()));
2019 
2020       const Point_3& xtrm1 = *sorted_points.begin();
2021       const Point_3& xtrm2 = *std::prev(sorted_points.end());
2022 
2023       // recover halfedges on the hole, bounded by the reference vertices
2024       std::vector<halfedge_descriptor> side_one, side_two;
2025 
2026       // look for the outgoing border halfedge of the first extreme point
2027       for(halfedge_descriptor hd : boundary_hedges)
2028       {
2029         if(get(vpmap, source(hd, tmesh)) == xtrm1)
2030         {
2031           side_one.push_back(hd);
2032           break;
2033         }
2034       }
2035 
2036       CGAL_assertion(side_one.size() == 1);
2037 
2038       bool non_monotone_border = false;
2039 
2040       while(get(vpmap, target(side_one.back(), tmesh)) != xtrm2)
2041       {
2042         vertex_descriptor prev_vertex = target(side_one.back(), tmesh);
2043         for(halfedge_descriptor hd : boundary_hedges)
2044         {
2045           if(source(hd, tmesh) == prev_vertex)
2046           {
2047             if(get(vpmap, target(hd, tmesh)) < get(vpmap, prev_vertex))
2048               non_monotone_border = true;
2049 
2050             side_one.push_back(hd);
2051             break;
2052           }
2053         }
2054 
2055         if(non_monotone_border)
2056           break;
2057       }
2058 
2059       if(non_monotone_border)
2060       {
2061 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
2062         std::cout << "  WARNING: Cannot remove the component of degenerate faces: border not a monotonic cycle.\n";
2063 #endif
2064 
2065         for(face_descriptor f : cc_faces)
2066           degenerate_face_set.erase(f);
2067 
2068         continue;
2069       }
2070 
2071       // look for the outgoing border halfedge of second extreme vertex
2072       for(halfedge_descriptor hd : boundary_hedges)
2073       {
2074         if(source(hd, tmesh) == target(side_one.back(), tmesh))
2075         {
2076           side_two.push_back(hd);
2077           break;
2078         }
2079       }
2080 
2081       CGAL_assertion(side_two.size() == 1);
2082 
2083       while(target(side_two.back(), tmesh) != source(side_one.front(), tmesh))
2084       {
2085         vertex_descriptor prev_vertex = target(side_two.back(), tmesh);
2086         for(halfedge_descriptor hd : boundary_hedges)
2087         {
2088           if(source(hd, tmesh) == prev_vertex)
2089           {
2090             if(get(vpmap, target(hd, tmesh)) > get(vpmap, prev_vertex))
2091               non_monotone_border = true;
2092 
2093             side_two.push_back(hd);
2094             break;
2095           }
2096         }
2097 
2098         if(non_monotone_border)
2099           break;
2100       }
2101 
2102       if(non_monotone_border)
2103       {
2104 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
2105         std::cout << "  WARNING: Cannot remove the component of degenerate faces: border not a monotonic cycle.\n";
2106 #endif
2107 
2108         for(face_descriptor f : cc_faces)
2109           degenerate_face_set.erase(f);
2110 
2111         continue;
2112       }
2113 
2114       CGAL_assertion(side_one.size()+side_two.size()==boundary_hedges.size());
2115 
2116       // reverse the order of the second side so as to follow
2117       // the same order than side one
2118       std::reverse(side_two.begin(), side_two.end());
2119       for(halfedge_descriptor& h : side_two)
2120         h = opposite(h, tmesh);
2121 
2122       //make sure the points of the vertices along side_one are correctly sorted
2123       std::vector<Point_3> side_points;
2124       side_points.reserve(side_one.size()+1);
2125       side_points.push_back(get(vpmap, source(side_one.front(), tmesh)));
2126 
2127       for(halfedge_descriptor h : side_one)
2128         side_points.push_back(get(vpmap, target(h, tmesh)));
2129 
2130       CGAL_assertion(get(vpmap,source(side_one.front(), tmesh)) == side_points.front());
2131       CGAL_assertion(get(vpmap,target(side_one.back(), tmesh)) == side_points.back());
2132 
2133       // @todo the reordering could lead to the apparition of null edges.
2134       std::sort(side_points.begin(), side_points.end());
2135 
2136       CGAL_assertion(std::unique(side_points.begin(), side_points.end())==side_points.end());
2137       for(std::size_t i=0; i<side_one.size()-1; ++i)
2138         put(vpmap, target(side_one[i], tmesh), side_points[i+1]);
2139 
2140       //same thing for side_two
2141       side_points.clear();
2142       side_points.reserve(side_two.size()+1);
2143       side_points.push_back(get(vpmap, source(side_two.front(), tmesh)));
2144 
2145       for(halfedge_descriptor h : side_two)
2146         side_points.push_back(get(vpmap,target(h, tmesh)));
2147 
2148       CGAL_assertion(get(vpmap, source(side_two.front(), tmesh)) == side_points.front());
2149       CGAL_assertion(get(vpmap, target(side_two.back(), tmesh)) == side_points.back());
2150 
2151       // @todo the reordering could lead to the apparition of null edges.
2152       std::sort(side_points.begin(), side_points.end());
2153 
2154       CGAL_assertion(std::unique(side_points.begin(), side_points.end())==side_points.end());
2155       for(std::size_t i=0; i<side_two.size()-1; ++i)
2156         put(vpmap, target(side_two[i], tmesh), side_points[i+1]);
2157 
2158       CGAL_assertion(source(side_one.front(), tmesh) == *ref_vertices.first);
2159       CGAL_assertion(source(side_two.front(), tmesh) == *ref_vertices.first);
2160       CGAL_assertion(target(side_one.back(), tmesh) == *ref_vertices.second);
2161       CGAL_assertion(target(side_two.back(), tmesh) == *ref_vertices.second);
2162 
2163       typename Sorted_point_set::iterator it_pt = std::next(sorted_points.begin()),
2164                                           it_pt_end = std::prev(sorted_points.end());
2165 
2166       bool non_collapsable = false;
2167       typename std::vector<halfedge_descriptor>::iterator side_one_it = side_one.begin();
2168       typename std::vector<halfedge_descriptor>::iterator side_two_it = side_two.begin();
2169       for(;it_pt!=it_pt_end;++it_pt)
2170       {
2171         // check if it_pt is the point of the target of one or two halfedges
2172         bool target_of_side_one = (get(vpmap, target(*side_one_it, tmesh)) == *it_pt);
2173         bool target_of_side_two = (get(vpmap, target(*side_two_it, tmesh)) == *it_pt);
2174 
2175         if(target_of_side_one && target_of_side_two)
2176         {
2177           for(halfedge_descriptor h : halfedges_around_target(*side_one_it, tmesh))
2178           {
2179             if(source(h, tmesh) == target(*side_two_it, tmesh))
2180             {
2181               non_collapsable = true;
2182               break;
2183             }
2184           }
2185         }
2186         else
2187         {
2188           CGAL_assertion(target_of_side_one || target_of_side_two);
2189           vertex_descriptor v1 = target_of_side_one ? target(*side_one_it, tmesh)
2190                                                     : target(*side_two_it, tmesh);
2191           vertex_descriptor v2 = target_of_side_two ? target(next(opposite(*side_one_it, tmesh), tmesh), tmesh)
2192                                                     : target(next(*side_two_it, tmesh), tmesh);
2193           for(halfedge_descriptor h : halfedges_around_target(v1, tmesh))
2194           {
2195             if(source(h, tmesh)==v2)
2196             {
2197               non_collapsable=true;
2198               break;
2199             }
2200           }
2201         }
2202 
2203         if(non_collapsable) break;
2204         if(target_of_side_one) ++side_one_it;
2205         if(target_of_side_two) ++side_two_it;
2206       }
2207 
2208       if(non_collapsable)
2209       {
2210         for(face_descriptor f : cc_faces)
2211           degenerate_face_set.erase(f);
2212 
2213 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
2214         std::cout << "  WARNING: cannot remove a connected components of degenerate faces.\n";
2215 #endif
2216         continue;
2217       }
2218 
2219       // now proceed to the fix
2220       // update the face and halfedge vertex pointers on the boundary
2221       for(halfedge_descriptor h : boundary_hedges)
2222       {
2223         set_face(h, GT::null_face(), tmesh);
2224         set_halfedge(target(h, tmesh), h, tmesh);
2225       }
2226 
2227       // update next/prev pointers of boundary_hedges
2228       for(halfedge_descriptor h : boundary_hedges)
2229       {
2230         halfedge_descriptor next_candidate = next(h, tmesh);
2231         while(face(next_candidate, tmesh)!=GT::null_face())
2232           next_candidate = next(opposite(next_candidate, tmesh), tmesh);
2233 
2234         set_next(h, next_candidate, tmesh);
2235       }
2236 
2237       // remove degenerate faces
2238       for(face_descriptor f : cc_faces)
2239       {
2240         degenerate_face_set.erase(f);
2241         remove_face(f, tmesh);
2242       }
2243 
2244       // remove interior edges
2245       for(halfedge_descriptor h : inside_hedges)
2246         remove_edge(edge(h, tmesh), tmesh);
2247 
2248       // remove interior vertices
2249       for(vertex_descriptor v : inside_vertices)
2250         remove_vertex(v, tmesh);
2251 
2252 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
2253       std::cout << "  side_one.size() " << side_one.size() << "\n";
2254       std::cout << "  side_two.size() " << side_two.size() << "\n";
2255 #endif
2256 
2257       CGAL_assertion(source(side_one.front(), tmesh) == *ref_vertices.first);
2258       CGAL_assertion(source(side_two.front(), tmesh) == *ref_vertices.first);
2259       CGAL_assertion(target(side_one.back(), tmesh) == *ref_vertices.second);
2260       CGAL_assertion(target(side_two.back(), tmesh) == *ref_vertices.second);
2261 
2262       // now split each side to contains the same sequence of points
2263       //    first side
2264       int hi = 0;
2265 
2266       for(typename Sorted_point_set::iterator it=std::next(sorted_points.begin()),
2267                                               it_end=sorted_points.end(); it!=it_end; ++it)
2268       {
2269         CGAL_assertion(*std::prev(it) == get(vpmap, source(side_one[hi], tmesh)));
2270 
2271         if(*it != get(vpmap, target(side_one[hi], tmesh)))
2272         {
2273           // split the edge and update the point
2274           halfedge_descriptor h1 = next(opposite(side_one[hi], tmesh), tmesh);
2275           put(vpmap, target(Euler::split_edge(side_one[hi], tmesh), tmesh), *it);
2276 
2277           // split_edge updates the halfedge of the source vertex of h,
2278           // since we reuse later the halfedge of the first refernce vertex
2279           // we must set it as we need.
2280           if(source(h1, tmesh) == *ref_vertices.first)
2281             set_halfedge(*ref_vertices.first, prev(prev(side_one[hi], tmesh), tmesh), tmesh);
2282 
2283           // retriangulate the opposite face
2284           if(face(h1, tmesh) != GT::null_face())
2285             Euler::split_face(h1, opposite(side_one[hi], tmesh), tmesh);
2286         }
2287         else
2288         {
2289           ++hi;
2290         }
2291       }
2292 
2293       //    second side
2294       hi = 0;
2295       for(typename Sorted_point_set::iterator it=std::next(sorted_points.begin()),
2296                                               it_end=sorted_points.end(); it!=it_end; ++it)
2297       {
2298         CGAL_assertion(*std::prev(it) == get(vpmap, source(side_two[hi], tmesh)));
2299         if(*it != get(vpmap, target(side_two[hi], tmesh)))
2300         {
2301           // split the edge and update the point
2302           halfedge_descriptor h2 = Euler::split_edge(side_two[hi], tmesh);
2303           put(vpmap, target(h2, tmesh), *it);
2304 
2305           // split_edge updates the halfedge of the source vertex of h,
2306           // since we reuse later the halfedge of the first refernce vertex
2307           // we must set it as we need.
2308           if(source(h2, tmesh) == *ref_vertices.first)
2309             set_halfedge(*ref_vertices.first, opposite(h2, tmesh), tmesh);
2310 
2311           // retriangulate the face
2312           if(face(h2, tmesh) != GT::null_face())
2313             Euler::split_face(h2, next(side_two[hi], tmesh), tmesh);
2314         }
2315         else
2316         {
2317           ++hi;
2318         }
2319       }
2320 
2321       CGAL_assertion(target(halfedge(*ref_vertices.first, tmesh), tmesh) == *ref_vertices.first);
2322       CGAL_assertion(face(halfedge(*ref_vertices.first, tmesh), tmesh) == GT::null_face());
2323 
2324 #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
2325       {
2326         halfedge_descriptor h_side2 = halfedge(*ref_vertices.first, tmesh);
2327         halfedge_descriptor h_side1 = next(h_side2, tmesh);
2328 
2329         do
2330         {
2331           CGAL_assertion(get(vpmap, source(h_side1, tmesh)) == get(vpmap, target(h_side2, tmesh)));
2332           CGAL_assertion(get(vpmap, target(h_side1, tmesh)) == get(vpmap, source(h_side2, tmesh)));
2333 
2334           if(target(next(opposite(h_side1, tmesh), tmesh), tmesh) ==
2335                target(next(opposite(h_side2, tmesh), tmesh), tmesh))
2336           {
2337             CGAL_assertion(!"Forbidden simplification");
2338           }
2339 
2340           h_side2 = prev(h_side2, tmesh);
2341           h_side1 = next(h_side1, tmesh);
2342         }
2343         while(target(h_side1, tmesh) != *ref_vertices.second);
2344       }
2345 #endif
2346 
2347       // remove side1 and replace its opposite hedges by those of side2
2348       halfedge_descriptor h_side2 = halfedge(*ref_vertices.first, tmesh);
2349       halfedge_descriptor h_side1 = next(h_side2, tmesh);
2350       for(;;)
2351       {
2352         CGAL_assertion(get(vpmap, source(h_side1, tmesh)) == get(vpmap, target(h_side2, tmesh)));
2353         CGAL_assertion(get(vpmap, target(h_side1, tmesh)) == get(vpmap, source(h_side2, tmesh)));
2354 
2355         // backup target vertex
2356         vertex_descriptor vertex_to_remove = target(h_side1, tmesh);
2357         if(vertex_to_remove!=*ref_vertices.second)
2358         {
2359           vertex_descriptor replacement_vertex = source(h_side2, tmesh);
2360           // replace the incident vertex
2361           for(halfedge_descriptor hd : halfedges_around_target(h_side1, tmesh))
2362             set_target(hd, replacement_vertex, tmesh);
2363         }
2364 
2365         // prev side2 hedge for next loop
2366         halfedge_descriptor h_side2_for_next_turn = prev(h_side2, tmesh);
2367 
2368         // replace the opposite of h_side1 by h_side2
2369         halfedge_descriptor opposite_h_side1 = opposite(h_side1, tmesh);
2370         face_descriptor the_face = face(opposite_h_side1, tmesh);
2371         set_face(h_side2, the_face, tmesh);
2372 
2373         if(the_face!=GT::null_face())
2374           set_halfedge(the_face, h_side2, tmesh);
2375 
2376         set_next(h_side2, next(opposite_h_side1, tmesh), tmesh);
2377         set_next(prev(opposite_h_side1, tmesh), h_side2, tmesh);
2378 
2379         // take the next hedges
2380         edge_descriptor edge_to_remove = edge(h_side1, tmesh);
2381         h_side1 = next(h_side1, tmesh);
2382 
2383         // now remove the extra edge
2384         remove_edge(edge_to_remove, tmesh);
2385 
2386         // ... and the extra vertex if it's not the second reference
2387         if(vertex_to_remove == *ref_vertices.second)
2388         {
2389           // update the halfedge pointer of the last vertex (others were already from side 2)
2390           CGAL_assertion(target(opposite(h_side2, tmesh), tmesh) == vertex_to_remove);
2391           set_halfedge(vertex_to_remove, opposite(h_side2, tmesh), tmesh);
2392           break;
2393         }
2394         else
2395         {
2396           remove_vertex(vertex_to_remove , tmesh);
2397         }
2398 
2399         h_side2 = h_side2_for_next_turn;
2400       }
2401     }
2402   }
2403 
2404   return all_removed;
2405 }
2406 
2407 template <typename FaceRange, typename TriangleMesh>
2408 bool remove_degenerate_faces(const FaceRange& face_range,
2409                              TriangleMesh& tmesh)
2410 {
2411   return remove_degenerate_faces(face_range, tmesh, CGAL::parameters::all_default());
2412 }
2413 
2414 template <typename TriangleMesh, typename CGAL_PMP_NP_TEMPLATE_PARAMETERS>
2415 bool remove_degenerate_faces(TriangleMesh& tmesh,
2416                              const CGAL_PMP_NP_CLASS& np)
2417 {
2418   return remove_degenerate_faces(faces(tmesh), tmesh, np);
2419 }
2420 
2421 template<typename TriangleMesh>
2422 bool remove_degenerate_faces(TriangleMesh& tmesh)
2423 {
2424   return remove_degenerate_faces(tmesh, CGAL::parameters::all_default());
2425 }
2426 
2427 } // namespace Polygon_mesh_processing
2428 } // namespace CGAL
2429 
2430 #endif // CGAL_POLYGON_MESH_PROCESSING_REPAIR_DEGENERACIES_H
2431