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