From 0db8941e0a276f7eebd3472886aed18cfa1f7250 Mon Sep 17 00:00:00 2001 From: Konstantinos Katrioplas Date: Tue, 10 Apr 2018 15:26:21 +0200 Subject: [PATCH 01/36] is_degenerate_edge function --- .../CGAL/Polygon_mesh_processing/repair.h | 48 +++++++++++++++++++ .../remove_degeneracies_test.cpp | 16 +++++++ 2 files changed, 64 insertions(+) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h index 6fcb1e929dd..02d71ec97a8 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h @@ -186,6 +186,54 @@ degenerate_faces(const TriangleMesh& tm, OutputIterator out) return degenerate_faces(tm, get(vertex_point, tm), Kernel(), out); } +/// \ingroup PMP_repairing_grp +/// checks whether an edge is degenerate. +/// An edge is considered degenerate if two of its vertices share the same location. +/// +/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph` +/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" +/// +/// @param pm the triangulated surface mesh to be repaired +/// @param np optional \ref pmp_namedparameters "Named Parameters" described below +/// +/// \cgalNamedParamsBegin +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `PolygonMesh` +/// \cgalParamEnd +/// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// The traits class must provide the nested type `Point_3`, +/// and the nested functor : +/// - `Equal_3` to check whether 2 points are identical +/// \cgalParamEnd +/// \cgalNamedParamsEnd +/// +/// \return true if the edge is degenerate +template +bool is_degenerate_edge(typename boost::graph_traits::edge_descriptor e, + PolygonMesh& pm, + const NamedParameters& np) +{ + using boost::get_param; + using boost::choose_param; + + typedef typename GetVertexPointMap::type VertexPointMap; + VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), + get_property_map(vertex_point, pm)); + typedef typename GetGeomTraits::type Traits; + Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits()); + + if ( traits.equal_3_object()(get(vpmap, target(e, pm)), get(vpmap, source(e, pm))) ) + return true; +} + +template +bool is_degenerate_edge(typename boost::graph_traits::edge_descriptor e, + PolygonMesh& pm) +{ + return is_degenerate_edge(e, pm, parameters::all_default()); +} + // this function remove a border edge even if it does not satisfy the link condition. // The only limitation is that the length connected component of the boundary this edge // is strictly greater than 3 diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp index 386e27723a1..3e069bf79be 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -29,6 +30,20 @@ void fix(const char* fname) assert( CGAL::is_valid_polygon_mesh(mesh) ); } +void check_edge_degeneracy(const char* fname) +{ + std::ifstream input(fname); + + Surface_mesh mesh; + if (!input || !(input >> mesh) || mesh.is_empty()) { + std::cerr << fname << " is not a valid off file.\n"; + exit(1); + } + + BOOST_FOREACH(typename boost::graph_traits::edge_descriptor e, edges(mesh)) + CGAL::Polygon_mesh_processing::is_degenerate_edge(e, mesh); +} + int main() { fix("data_degeneracies/degtri_2dt_1edge_split_twice.off"); @@ -38,6 +53,7 @@ int main() fix("data_degeneracies/degtri_three.off"); fix("data_degeneracies/degtri_single.off"); fix("data_degeneracies/trihole.off"); + check_edge_degeneracy("data_degeneracies/degtri_edge.off"); return 0; } From 8e285cb1a778c22979a64d43b0bd0026c3b80e3b Mon Sep 17 00:00:00 2001 From: Konstantinos Katrioplas Date: Tue, 10 Apr 2018 16:58:13 +0200 Subject: [PATCH 02/36] is_degenerate_triangle_face function --- .../CGAL/Polygon_mesh_processing/repair.h | 56 ++++++++++++++++++- .../remove_degeneracies_test.cpp | 15 +++++ 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h index 02d71ec97a8..d12e1321a95 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h @@ -193,6 +193,7 @@ degenerate_faces(const TriangleMesh& tm, OutputIterator out) /// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph` /// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" /// +/// @param e the edge to check whether is degenerate /// @param pm the triangulated surface mesh to be repaired /// @param np optional \ref pmp_namedparameters "Named Parameters" described below /// @@ -234,6 +235,57 @@ bool is_degenerate_edge(typename boost::graph_traits::edge_descript return is_degenerate_edge(e, pm, parameters::all_default()); } +/// \ingroup PMP_repairing_grp +/// checks whether a triangle face is degenerate. +/// A triangle face is considered degenerate if all three points of the face are collinear. +/// +/// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph` +/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" +/// +/// @param f the face to check whether is degenerate +/// @param tm the triangulated surface mesh to be repaired +/// @param np optional \ref pmp_namedparameters "Named Parameters" described below +/// +/// \cgalNamedParamsBegin +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `PolygonMesh` +/// \cgalParamEnd +/// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// The traits class must provide the nested type `Point_3`, +/// and the nested functor : +/// - `Collinear_3` to check whether 3 points are collinear +/// \cgalParamEnd +/// \cgalNamedParamsEnd +/// +/// \return true if the triangle face is degenerate +template +bool is_degenerate_triangle_face(typename boost::graph_traits::face_descriptor f, + TriangleMesh& tm, + const NamedParameters& np) +{ + CGAL_assertion(CGAL::is_triangle_mesh(tm)); + + using boost::get_param; + using boost::choose_param; + + typedef typename GetVertexPointMap::type VertexPointMap; + VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), + get_property_map(vertex_point, tm)); + typedef typename GetGeomTraits::type Traits; + Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits()); + + // call from BGL helpers + return is_degenerate_triangle_face(f, tm, vpmap, traits); +} + +template +bool is_degenerate_triangle_face(typename boost::graph_traits::face_descriptor f, + TriangleMesh& tm) +{ + return CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, tm, parameters::all_default()); +} + // this function remove a border edge even if it does not satisfy the link condition. // The only limitation is that the length connected component of the boundary this edge // is strictly greater than 3 @@ -780,7 +832,7 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh, // Then, remove triangles made of 3 collinear points std::set degenerate_face_set; BOOST_FOREACH(face_descriptor fd, faces(tmesh)) - if ( is_degenerate_triangle_face(fd, tmesh, vpmap, traits) ) + if ( is_degenerate_triangle_face(fd, tmesh) ) degenerate_face_set.insert(fd); nb_deg_faces+=degenerate_face_set.size(); @@ -811,7 +863,7 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh, degenerate_face_set.erase( face(hd2, tmesh) ); // remove the central vertex and check if the new face is degenerated hd=CGAL::Euler::remove_center_vertex(hd, tmesh); - if (is_degenerate_triangle_face(face(hd, tmesh), tmesh, vpmap, traits)) + if (is_degenerate_triangle_face(face(hd, tmesh), tmesh)) { degenerate_face_set.insert( face(hd, tmesh) ); } diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp index 3e069bf79be..e87bd9b2b83 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp @@ -44,6 +44,20 @@ void check_edge_degeneracy(const char* fname) CGAL::Polygon_mesh_processing::is_degenerate_edge(e, mesh); } +void check_triangle_face_degeneracy(const char* fname) +{ + std::ifstream input(fname); + + Surface_mesh mesh; + if (!input || !(input >> mesh) || mesh.is_empty()) { + std::cerr << fname << " is not a valid off file.\n"; + exit(1); + } + + BOOST_FOREACH(typename boost::graph_traits::face_descriptor f, faces(mesh)) + CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, mesh); +} + int main() { fix("data_degeneracies/degtri_2dt_1edge_split_twice.off"); @@ -54,6 +68,7 @@ int main() fix("data_degeneracies/degtri_single.off"); fix("data_degeneracies/trihole.off"); check_edge_degeneracy("data_degeneracies/degtri_edge.off"); + check_triangle_face_degeneracy("data_degeneracies/degtri_four.off"); return 0; } From 9f315abad6d417634a83fce864164c2cceda654f Mon Sep 17 00:00:00 2001 From: Konstantinos Katrioplas Date: Tue, 10 Apr 2018 17:40:30 +0200 Subject: [PATCH 03/36] duplicate_vertices function doc --- .../CGAL/Polygon_mesh_processing/repair.h | 40 +++++++++++++++---- .../remove_degeneracies_test.cpp | 14 +++++++ 2 files changed, 46 insertions(+), 8 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h index d12e1321a95..9b7fcfa4c78 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h @@ -157,8 +157,6 @@ struct Less_vertex_point{ } }; -///\cond SKIP_IN_MANUAL - template OutputIterator degenerate_faces(const TriangleMesh& tm, @@ -1381,7 +1379,6 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh, return nb_deg_faces; } - template std::size_t remove_degenerate_faces(TriangleMesh& tmesh) { @@ -1389,9 +1386,38 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh) CGAL::Polygon_mesh_processing::parameters::all_default()); } -template -std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, Vpm vpm) +/// \ingroup PMP_repairing_grp +/// duplicates all non-manifold vertices of the input mesh. +/// +/// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph` +/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" +/// +/// @param tm the triangulated surface mesh to be repaired +/// @param np optional \ref pmp_namedparameters "Named Parameters" described below +/// +/// \cgalNamedParamsBegin +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `PolygonMesh` +/// \cgalParamEnd +/// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// \cgalParamEnd +/// \cgalNamedParamsEnd +/// +/// \return true if the triangle face is degenerate +template +std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, + const NamedParameters& np) { + CGAL_assertion(CGAL::is_triangle_mesh(tm)); + + using boost::get_param; + using boost::choose_param; + + typedef typename GetVertexPointMap::type VertexPointMap; + VertexPointMap vpm = choose_param(get_param(np, internal_np::vertex_point), + get_property_map(vertex_point, tm)); + typedef boost::graph_traits GT; typedef typename GT::vertex_descriptor vertex_descriptor; typedef typename GT::halfedge_descriptor halfedge_descriptor; @@ -1443,11 +1469,9 @@ std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, Vpm vpm) template std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm) { - return duplicate_non_manifold_vertices(tm, get(vertex_point, tm)); + return duplicate_non_manifold_vertices(tm, parameters::all_default()); } -/// \endcond - /// \ingroup PMP_repairing_grp /// removes the isolated vertices from any polygon mesh. diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp index e87bd9b2b83..8f89842f922 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp @@ -58,6 +58,19 @@ void check_triangle_face_degeneracy(const char* fname) CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, mesh); } +void test_vetices_duplication(const char* fname) +{ + std::ifstream input(fname); + + Surface_mesh mesh; + if (!input || !(input >> mesh) || mesh.is_empty()) { + std::cerr << fname << " is not a valid off file.\n"; + exit(1); + } + + CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices(mesh); +} + int main() { fix("data_degeneracies/degtri_2dt_1edge_split_twice.off"); @@ -69,6 +82,7 @@ int main() fix("data_degeneracies/trihole.off"); check_edge_degeneracy("data_degeneracies/degtri_edge.off"); check_triangle_face_degeneracy("data_degeneracies/degtri_four.off"); + test_vetices_duplication("data_degeneracies/degtri_four.off"); return 0; } From c3e7f6d94b09b71bf5df281c7fba50628d9436d0 Mon Sep 17 00:00:00 2001 From: Konstantinos Katrioplas Date: Wed, 11 Apr 2018 12:04:21 +0200 Subject: [PATCH 04/36] is_non_manifold_vertex function --- .../CGAL/Polygon_mesh_processing/repair.h | 54 +++++++++++++++---- .../remove_degeneracies_test.cpp | 15 ++++++ 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h index 9b7fcfa4c78..aef378dee83 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h @@ -184,6 +184,40 @@ degenerate_faces(const TriangleMesh& tm, OutputIterator out) return degenerate_faces(tm, get(vertex_point, tm), Kernel(), out); } +/// \ingroup PMP_repairing_grp +/// checks whether a vertex is non-manifold. +/// +/// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph` +/// +/// @param v the vertex to check whether is degenerate +/// @param tm the triangulated surface mesh upon evaluation +/// +/// \return true if the vertrex is non-manifold +template +bool is_non_manifold_vertex(typename boost::graph_traits::vertex_descriptor v, + const TriangleMesh& tm) +{ + CGAL_assertion(CGAL::is_triangle_mesh(tm)); + + typedef boost::graph_traits GT; + typedef typename GT::halfedge_descriptor halfedge_descriptor; + + boost::unordered_set halfedges_handled; + halfedge_descriptor start = halfedge(v, tm); + halfedge_descriptor h=start; + do{ + halfedges_handled.insert(h); + h=opposite(next(h, tm), tm); + }while(h != start); + + BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(v, tm)) + { + if(!halfedges_handled.count(h)) + return true; + } + return false; +} + /// \ingroup PMP_repairing_grp /// checks whether an edge is degenerate. /// An edge is considered degenerate if two of its vertices share the same location. @@ -192,7 +226,7 @@ degenerate_faces(const TriangleMesh& tm, OutputIterator out) /// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" /// /// @param e the edge to check whether is degenerate -/// @param pm the triangulated surface mesh to be repaired +/// @param pm the triangulated surface mesh upon evaluation /// @param np optional \ref pmp_namedparameters "Named Parameters" described below /// /// \cgalNamedParamsBegin @@ -210,15 +244,15 @@ degenerate_faces(const TriangleMesh& tm, OutputIterator out) /// \return true if the edge is degenerate template bool is_degenerate_edge(typename boost::graph_traits::edge_descriptor e, - PolygonMesh& pm, + const PolygonMesh& pm, const NamedParameters& np) { using boost::get_param; using boost::choose_param; - typedef typename GetVertexPointMap::type VertexPointMap; + typedef typename GetVertexPointMap::const_type VertexPointMap; VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), - get_property_map(vertex_point, pm)); + get_const_property_map(vertex_point, pm)); typedef typename GetGeomTraits::type Traits; Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits()); @@ -228,7 +262,7 @@ bool is_degenerate_edge(typename boost::graph_traits::edge_descript template bool is_degenerate_edge(typename boost::graph_traits::edge_descriptor e, - PolygonMesh& pm) + const PolygonMesh& pm) { return is_degenerate_edge(e, pm, parameters::all_default()); } @@ -241,7 +275,7 @@ bool is_degenerate_edge(typename boost::graph_traits::edge_descript /// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" /// /// @param f the face to check whether is degenerate -/// @param tm the triangulated surface mesh to be repaired +/// @param tm the triangulated surface mesh upon evaluation /// @param np optional \ref pmp_namedparameters "Named Parameters" described below /// /// \cgalNamedParamsBegin @@ -259,7 +293,7 @@ bool is_degenerate_edge(typename boost::graph_traits::edge_descript /// \return true if the triangle face is degenerate template bool is_degenerate_triangle_face(typename boost::graph_traits::face_descriptor f, - TriangleMesh& tm, + const TriangleMesh& tm, const NamedParameters& np) { CGAL_assertion(CGAL::is_triangle_mesh(tm)); @@ -267,9 +301,9 @@ bool is_degenerate_triangle_face(typename boost::graph_traits::fac using boost::get_param; using boost::choose_param; - typedef typename GetVertexPointMap::type VertexPointMap; + typedef typename GetVertexPointMap::const_type VertexPointMap; VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), - get_property_map(vertex_point, tm)); + get_const_property_map(vertex_point, tm)); typedef typename GetGeomTraits::type Traits; Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits()); @@ -279,7 +313,7 @@ bool is_degenerate_triangle_face(typename boost::graph_traits::fac template bool is_degenerate_triangle_face(typename boost::graph_traits::face_descriptor f, - TriangleMesh& tm) + const TriangleMesh& tm) { return CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, tm, parameters::all_default()); } diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp index 8f89842f922..6f2c49341d9 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp @@ -71,6 +71,20 @@ void test_vetices_duplication(const char* fname) CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices(mesh); } +void test_vertex_non_manifoldness(const char* fname) +{ + std::ifstream input(fname); + + Surface_mesh mesh; + if (!input || !(input >> mesh) || mesh.is_empty()) { + std::cerr << fname << " is not a valid off file.\n"; + exit(1); + } + + BOOST_FOREACH(typename boost::graph_traits::vertex_descriptor v, vertices(mesh)) + CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh); +} + int main() { fix("data_degeneracies/degtri_2dt_1edge_split_twice.off"); @@ -83,6 +97,7 @@ int main() check_edge_degeneracy("data_degeneracies/degtri_edge.off"); check_triangle_face_degeneracy("data_degeneracies/degtri_four.off"); test_vetices_duplication("data_degeneracies/degtri_four.off"); + test_vertex_non_manifoldness("data/non_manifold_vertex.off");; return 0; } From 1f0628fad247d3f7b7c199964b0b4a71f18b5176 Mon Sep 17 00:00:00 2001 From: Konstantinos Katrioplas Date: Thu, 12 Apr 2018 10:24:48 +0200 Subject: [PATCH 05/36] is needle andcap functions --- .../CGAL/Polygon_mesh_processing/repair.h | 149 ++++++++++++++++++ .../remove_degeneracies_test.cpp | 49 +++++- 2 files changed, 196 insertions(+), 2 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h index aef378dee83..d13eff11f9f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h @@ -318,6 +318,155 @@ bool is_degenerate_triangle_face(typename boost::graph_traits::fac return CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, tm, parameters::all_default()); } +/// \ingroup PMP_repairing_grp +/// checks whether a triangle face is needle-like. +/// In a needle-like triangle its longest edge is much longer than the shortest one. +/// +/// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph` +/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" +/// +/// @param f the face to check whether is almost degenerate +/// @param tm the triangulated surface mesh upon evaluation +/// @param threshold a number in the range [0, 1] to indicate the tolerance +/// upon which to characterize the degeneracy. 1 means that needle triangles +/// are those that have a infinitely small edge, while 0 means that needle triangles +/// are those that would have an infinitely long edge +/// @param np optional \ref pmp_namedparameters "Named Parameters" described below +/// +/// \cgalNamedParamsBegin +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `PolygonMesh` +/// \cgalParamEnd +/// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// \cgalParamEnd +/// \cgalNamedParamsEnd +/// +/// \return true if the triangle face is almost degenerate +template +bool is_needle_triangle_face(typename boost::graph_traits::face_descriptor f, + const TriangleMesh& tm, + const double threshold, + const NamedParameters& np) +{ + CGAL_assertion(CGAL::is_triangle_mesh(tm)); + + using boost::get_param; + using boost::choose_param; + + typedef typename GetVertexPointMap::const_type VertexPointMap; + VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), + get_const_property_map(vertex_point, tm)); + typedef typename GetGeomTraits::type FT; + typedef boost::graph_traits GT; + typedef typename GT::halfedge_descriptor halfedge_descriptor; + typedef typename GT::vertex_descriptor vertex_descriptor; + typedef typename boost::property_traits::value_type Point; + typedef typename Kernel_traits::Kernel::Vector_3 Vector; + + BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(halfedge(f, tm), tm)) + { + vertex_descriptor v0 = source(h, tm); + vertex_descriptor v1 = target(h, tm); + vertex_descriptor v2 = target(next(h, tm), tm); + + Vector a = get(vpmap, v0) - get(vpmap, v1); + Vector b = get(vpmap, v2) - get(vpmap, v1); + double ab = a*b; + double aa = a.squared_length(); + double bb = b.squared_length(); + double dot_ab = a*b / (CGAL::sqrt(aa) * CGAL::sqrt(bb)); + + // threshold = 1 means no tolerance, totally degenerate + if(dot_ab > threshold) + return true; + } + return false; +} + +template +bool is_needle_triangle_face(typename boost::graph_traits::face_descriptor f, + const TriangleMesh& tm, + const double threshold) +{ + is_needle_triangle_face(f, tm, threshold, parameters::all_default()); +} + +/// \ingroup PMP_repairing_grp +/// checks whether a triangle face is cap-like. +/// A cap-like triangle has an angle very close to 180 degrees. +/// +/// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph` +/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" +/// +/// @param f the face to check whether is almost degenerate +/// @param tm the triangulated surface mesh upon evaluation +/// @param threshold a number in the range [0, 1] to indicate the tolerance +/// upon which to characterize the degeneracy. 1 means that cap triangles +/// are considered those whose vertices for an angle of 180 degrees, while 0 means that +/// all triangles are considered caps. +/// @param np optional \ref pmp_namedparameters "Named Parameters" described below +/// +/// \cgalNamedParamsBegin +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `PolygonMesh` +/// \cgalParamEnd +/// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// \cgalParamEnd +/// \cgalNamedParamsEnd +/// +/// \return true if the triangle face is almost degenerate +template +bool is_cap_triangle_face(typename boost::graph_traits::face_descriptor f, + const TriangleMesh& tm, + const double threshold, + const NamedParameters& np) +{ + CGAL_assertion(CGAL::is_triangle_mesh(tm)); + + using boost::get_param; + using boost::choose_param; + + typedef typename GetVertexPointMap::const_type VertexPointMap; + VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), + get_const_property_map(vertex_point, tm)); + typedef typename GetGeomTraits::type FT; + typedef boost::graph_traits GT; + typedef typename GT::halfedge_descriptor halfedge_descriptor; + typedef typename GT::vertex_descriptor vertex_descriptor; + typedef typename boost::property_traits::value_type Point; + typedef typename Kernel_traits::Kernel::Vector_3 Vector; + + BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(halfedge(f, tm), tm)) + { + vertex_descriptor v0 = source(h, tm); + vertex_descriptor v1 = target(h, tm); + vertex_descriptor v2 = target(next(h, tm), tm); + + Vector a = get(vpmap, v0) - get(vpmap, v1); + Vector b = get(vpmap, v2) - get(vpmap, v1); + double ab = a*b; + double aa = a.squared_length(); + double bb = b.squared_length(); + double dot_ab = a*b / (CGAL::sqrt(aa) * CGAL::sqrt(bb)); + + // threshold = 1 means no tolerance, totally degenerate + // take the opposite, because cos it -1 at 180 degrees + if(dot_ab < -threshold) + return true; + } + return false; +} + +template +bool is_cap_triangle_face(typename boost::graph_traits::face_descriptor f, + const TriangleMesh& tm, + const double threshold) +{ + is_cap_triangle_face(f, tm, threshold, parameters::all_default()); +} + // this function remove a border edge even if it does not satisfy the link condition. // The only limitation is that the length connected component of the boundary this edge // is strictly greater than 3 diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp index 6f2c49341d9..82c275c9702 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp @@ -82,9 +82,52 @@ void test_vertex_non_manifoldness(const char* fname) } BOOST_FOREACH(typename boost::graph_traits::vertex_descriptor v, vertices(mesh)) - CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh); + { + if(CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh)) + std::cout << "true\n"; + + } } +void test_needle(const char* fname) +{ + std::ifstream input(fname); + + Surface_mesh mesh; + if (!input || !(input >> mesh) || mesh.is_empty()) { + std::cerr << fname << " is not a valid off file.\n"; + exit(1); + } + + double threshold = 0.8; + + BOOST_FOREACH(typename boost::graph_traits::face_descriptor f, faces(mesh)) + { + if(CGAL::Polygon_mesh_processing::is_needle_triangle_face(f, mesh, threshold)) + std::cout << "needle\n"; + } +} + +void test_cap(const char* fname) +{ + std::ifstream input(fname); + + Surface_mesh mesh; + if (!input || !(input >> mesh) || mesh.is_empty()) { + std::cerr << fname << " is not a valid off file.\n"; + exit(1); + } + + double threshold = 0.8; + + BOOST_FOREACH(typename boost::graph_traits::face_descriptor f, faces(mesh)) + { + if(CGAL::Polygon_mesh_processing::is_cap_triangle_face(f, mesh, threshold)) + std::cout << "cap\n"; + } +} + + int main() { fix("data_degeneracies/degtri_2dt_1edge_split_twice.off"); @@ -97,7 +140,9 @@ int main() check_edge_degeneracy("data_degeneracies/degtri_edge.off"); check_triangle_face_degeneracy("data_degeneracies/degtri_four.off"); test_vetices_duplication("data_degeneracies/degtri_four.off"); - test_vertex_non_manifoldness("data/non_manifold_vertex.off");; + test_vertex_non_manifoldness("data/non_manifold_vertex.off"); + test_needle("data_degeneracies/needle.off"); + test_cap("data_degeneracies/cap.off"); return 0; } From b4da4a21540f38a388f7560e74d00e0c4a263363 Mon Sep 17 00:00:00 2001 From: Konstantinos Katrioplas Date: Thu, 12 Apr 2018 17:27:19 +0200 Subject: [PATCH 06/36] add a couple of tests to cmakelists --- .../test/Polygon_mesh_processing/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt index f7af9794708..5ab90c954a9 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt @@ -100,6 +100,8 @@ endif() create_single_source_cgal_program("surface_intersection_sm_poly.cpp" ) create_single_source_cgal_program("test_orient_cc.cpp") create_single_source_cgal_program("test_pmp_transform.cpp") + create_single_source_cgal_program("remove_degeneracies_test.cpp") + create_single_source_cgal_program("remove_identical_test.cpp") if( TBB_FOUND ) CGAL_target_use_TBB(test_pmp_distance) From c79add2c6a65b1ba5b54312ad3359653d383fc1a Mon Sep 17 00:00:00 2001 From: Konstantinos Katrioplas Date: Thu, 12 Apr 2018 17:31:42 +0200 Subject: [PATCH 07/36] merge vertices, tests & data --- .../Polygon_mesh_processing/stitch_holes.h | 104 ++++++++++++++++++ .../data/merge_points.off | 10 ++ .../data_degeneracies/degtri_edge.off | 6 + .../remove_identical_test.cpp | 75 +++++++++++++ 4 files changed, 195 insertions(+) create mode 100644 Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_holes.h create mode 100644 Polygon_mesh_processing/test/Polygon_mesh_processing/data/merge_points.off create mode 100644 Polygon_mesh_processing/test/Polygon_mesh_processing/data_degeneracies/degtri_edge.off create mode 100644 Polygon_mesh_processing/test/Polygon_mesh_processing/remove_identical_test.cpp diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_holes.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_holes.h new file mode 100644 index 00000000000..af71d0bf408 --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_holes.h @@ -0,0 +1,104 @@ +#ifndef CGAL_STITCH_HOLES_H +#define CGAL_STITCH_HOLES _H + + +#include +#include +#include +#include + + +namespace CGAL{ + +namespace Polygon_mesh_processing{ + + +template +void extract_connected_components(PolygonMesh& mesh, + OutputIterator out) +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + std::set border_halfedges; + + BOOST_FOREACH(halfedge_descriptor h, halfedges(mesh)) + { + if(is_border(h, mesh)) + border_halfedges.insert(h); + } + + std::set connected_component; + BOOST_FOREACH(halfedge_descriptor h, border_halfedges) + { + if(connected_component.insert(h).second) + { + halfedge_descriptor start = h; + do{ + h = next(h, mesh); + connected_component.insert(h); + } while(h != start); + + *out++=connected_component; + } + } +} + + +template +std::size_t count_identical_points(PolygonMesh& mesh, + std::vector cc_list) +{ + // cc is a std::vector > + + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + + typedef typename boost::property_map::const_type Vpm; + Vpm vpm = get(boost::vertex_point, mesh); + + for(std::set i_cc : cc_list) + { + // for each cc + BOOST_FOREACH(halfedge_descriptor h, i_cc) + { + vertex_descriptor vs = source(h, mesh); + vertex_descriptor vt = target(h, mesh); + + // find identicals + } + } + return 0; // how many found +} + +/// \ingroup PMP_repairing_grp +/// merges two vertices into one +/// +/// @tparam TriangleMesh a model of `FaceListGraph` +/// +/// @param mesh the input triangle mesh +/// @param v_keep the vertex to be kept +/// @param v_rm the vertex to be removed +template +void merge_identical_points(PolygonMesh& mesh, + typename boost::graph_traits::vertex_descriptor v_keep, + typename boost::graph_traits::vertex_descriptor v_rm) +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + halfedge_descriptor h = halfedge(v_rm, mesh); + halfedge_descriptor start = h; + + do{ + set_target(h, v_keep, mesh); + h = opposite(next(h, mesh), mesh); + } while( h != start ); + + remove_vertex(v_rm, mesh); +} + + + + +} +} + +#endif //CGAL_STITCH_HOLES_H diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/data/merge_points.off b/Polygon_mesh_processing/test/Polygon_mesh_processing/data/merge_points.off new file mode 100644 index 00000000000..d520c25cdf2 --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/data/merge_points.off @@ -0,0 +1,10 @@ +OFF +6 2 0 +0 0 0 +1 0 0 +1 0 0 +2 0 0 +0.5 1 0 +1.5 1 0 +3 0 1 4 +3 2 3 5 diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/data_degeneracies/degtri_edge.off b/Polygon_mesh_processing/test/Polygon_mesh_processing/data_degeneracies/degtri_edge.off new file mode 100644 index 00000000000..afd48de232f --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/data_degeneracies/degtri_edge.off @@ -0,0 +1,6 @@ +OFF +3 1 0 +0 0 0 +1 0 0 +0 0 0 +3 0 1 2 diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_identical_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_identical_test.cpp new file mode 100644 index 00000000000..ae31ad7f8c8 --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_identical_test.cpp @@ -0,0 +1,75 @@ +#include +#include + +#include +#include + +#include + + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Surface_mesh; + + +void test_connected_components(const char* fname) +{ + std::ifstream input(fname); + + Surface_mesh mesh; + if (!input || !(input >> mesh) || mesh.is_empty()) { + std::cerr << fname << " is not a valid off file.\n"; + exit(1); + } + + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + + std::vector > connected_components; + + CGAL::Polygon_mesh_processing::extract_connected_components(mesh, + std::back_inserter(connected_components)); + + std::cout << "# cc = " << connected_components.size(); + + for(auto set : connected_components) + { + std::cout << "of size= " << set.size() << std::endl; + } +} + + +void test_merge_points(const char* fname) +{ + std::ifstream input(fname); + + Surface_mesh mesh; + if (!input || !(input >> mesh) || mesh.is_empty()) { + std::cerr << fname << " is not a valid off file.\n"; + exit(1); + } + + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + std::vector verts(vertices(mesh).begin(), vertices(mesh).end()); + + vertex_descriptor v_rm = verts[1]; + vertex_descriptor v_keep = verts[2]; + + CGAL::Polygon_mesh_processing::merge_identical_points(mesh, v_keep, v_rm); + + std::ofstream out("/tmp/result.off"); + out << mesh; + out.close(); +} + + +int main() +{ + + + test_connected_components("data/small_ex.off"); + test_merge_points("data/merge_points.off"); + + + + return 0; +} From af6576047505933b2f8d9dc6e5c5c702e4a5814e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 13 Apr 2018 13:44:53 +0200 Subject: [PATCH 08/36] rewrite boundary cycle merging --- .../Polygon_mesh_processing/stitch_holes.h | 203 ++++++++++++------ .../data/merge_points.off | 103 ++++++++- .../remove_identical_test.cpp | 60 ++---- 3 files changed, 243 insertions(+), 123 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_holes.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_holes.h index af71d0bf408..eb2010ed104 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_holes.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_holes.h @@ -1,104 +1,171 @@ -#ifndef CGAL_STITCH_HOLES_H -#define CGAL_STITCH_HOLES _H +// Copyright (c) 2018 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0+ +// +// +// Author(s) : Sebastien Loriot +#ifndef CGAL_STITCH_HOLES_H +#define CGAL_STITCH_HOLES_H #include #include #include #include +#include +#include +#include namespace CGAL{ namespace Polygon_mesh_processing{ - +/// \todo document me +/// It should probably go into BGL package template -void extract_connected_components(PolygonMesh& mesh, - OutputIterator out) +OutputIterator +extract_boundary_cycles(PolygonMesh& pm, + OutputIterator out) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - std::set border_halfedges; - - BOOST_FOREACH(halfedge_descriptor h, halfedges(mesh)) + boost::unordered_set hedge_handled; + BOOST_FOREACH(halfedge_descriptor h, halfedges(pm)) { - if(is_border(h, mesh)) - border_halfedges.insert(h); - } - - std::set connected_component; - BOOST_FOREACH(halfedge_descriptor h, border_halfedges) - { - if(connected_component.insert(h).second) + if(is_border(h, pm) && hedge_handled.insert(h).second) { - halfedge_descriptor start = h; - do{ - h = next(h, mesh); - connected_component.insert(h); - } while(h != start); - - *out++=connected_component; + *out++=h; + BOOST_FOREACH(halfedge_descriptor h2, halfedges_around_face(h, pm)) + hedge_handled.insert(h2); } } -} - - -template -std::size_t count_identical_points(PolygonMesh& mesh, - std::vector cc_list) -{ - // cc is a std::vector > - - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - - typedef typename boost::property_map::const_type Vpm; - Vpm vpm = get(boost::vertex_point, mesh); - - for(std::set i_cc : cc_list) - { - // for each cc - BOOST_FOREACH(halfedge_descriptor h, i_cc) - { - vertex_descriptor vs = source(h, mesh); - vertex_descriptor vt = target(h, mesh); - - // find identicals - } - } - return 0; // how many found + return out; } /// \ingroup PMP_repairing_grp -/// merges two vertices into one -/// -/// @tparam TriangleMesh a model of `FaceListGraph` -/// -/// @param mesh the input triangle mesh -/// @param v_keep the vertex to be kept -/// @param v_rm the vertex to be removed -template -void merge_identical_points(PolygonMesh& mesh, - typename boost::graph_traits::vertex_descriptor v_keep, - typename boost::graph_traits::vertex_descriptor v_rm) +/// \todo document me +template +void merge_vertices(const VertexRange& vertices, + PolygonMesh& pm) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - halfedge_descriptor h = halfedge(v_rm, mesh); - halfedge_descriptor start = h; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + vertex_descriptor v_kept=*boost::begin(vertices); + std::vector vertices_to_rm; + + BOOST_FOREACH(vertex_descriptor vd, vertices) + { + if (vd==v_kept) continue; // skip identical vertices + if (edge(vd, v_kept, pm).second) continue; // skip null edges + bool shall_continue=false; + BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(v_kept, pm)) + { + if (edge(vd, source(hd, pm), pm).second) + { + shall_continue=true; + break; + } + } + if (shall_continue) continue; // skip vertices already incident to the same vertex + + internal::update_target_vertex(halfedge(vd, pm), v_kept, pm); + vertices_to_rm.push_back(vd); + } + + BOOST_FOREACH(vertex_descriptor vd, vertices_to_rm) + remove_vertex(vd, pm); +} + +/// \ingroup PMP_repairing_grp +/// \todo document me +template +void merge_duplicated_vertices_in_boundary_cycle(typename boost::graph_traits::halfedge_descriptor h, + PolygonMesh& pm, + const NamedParameter& np) +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename GetVertexPointMap::const_type Vpm; + typedef typename boost::property_traits::value_type Point_3; + Vpm vpm = choose_param(get_param(np, internal_np::vertex_point), + get_const_property_map(vertex_point, pm)); + + // collect all the vertices of the cycle + std::vector vertices; + halfedge_descriptor start=h; do{ - set_target(h, v_keep, mesh); - h = opposite(next(h, mesh), mesh); - } while( h != start ); + vertices.push_back(target(h,pm)); + h=next(h, pm); + }while(start!=h); - remove_vertex(v_rm, mesh); + // sort vertices using their point to ease the detection + // of vertices with identical points + CGAL::Property_map_to_unary_function Get_point(vpm); + std::sort( vertices.begin(), vertices.end(), + boost::bind(std::less(), boost::bind(Get_point,_1), + boost::bind(Get_point, _2)) ); + std::size_t nbv=vertices.size(); + std::size_t i=1; + + std::vector< std::vector > identical_vertices; + while(i!=nbv) + { + if (get(vpm, vertices[i]) == get(vpm, vertices[i-1])) + { + identical_vertices.push_back( std::vector() ); + identical_vertices.back().push_back(vertices[i-1]); + identical_vertices.back().push_back(vertices[i]); + while(++i!=nbv) + { + if (get(vpm, vertices[i]) == get(vpm, vertices[i-1])) + identical_vertices.back().push_back(vertices[i]); + else + break; + } + } + ++i; + } + BOOST_FOREACH(const std::vector& vrtcs, identical_vertices) + merge_vertices(vrtcs, pm); } +/// \ingroup PMP_repairing_grp +/// \todo document me +template +void merge_duplicated_vertices_in_boundary_cycles( PolygonMesh& pm, + const NamedParameter& np) +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + std::vector cycles; + extract_boundary_cycles(pm, std::back_inserter(cycles)); - + BOOST_FOREACH(halfedge_descriptor h, cycles) + merge_duplicated_vertices_in_boundary_cycle(h, pm, np); } + +template +void merge_duplicated_vertices_in_boundary_cycles(PolygonMesh& pm) +{ + merge_duplicated_vertices_in_boundary_cycles(pm, parameters::all_default()); } +} } // end of CGAL::Polygon_mesh_processing + #endif //CGAL_STITCH_HOLES_H diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/data/merge_points.off b/Polygon_mesh_processing/test/Polygon_mesh_processing/data/merge_points.off index d520c25cdf2..7a83712189f 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/data/merge_points.off +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/data/merge_points.off @@ -1,10 +1,95 @@ OFF -6 2 0 -0 0 0 -1 0 0 -1 0 0 -2 0 0 -0.5 1 0 -1.5 1 0 -3 0 1 4 -3 2 3 5 +47 45 0 + +-0.026804100000000001 -0.30492799999999998 0.142542 +-0.018234400000000001 -0.28125499999999998 0.15192700000000001 +-0.074329900000000004 -0.27701599999999998 0.17852999999999999 +-0.075232300000000002 -0.238787 0.19677600000000001 +-0.092496499999999995 -0.26196000000000003 0.18681900000000001 +-0.107975 -0.220055 0.202264 +-0.098042799999999999 -0.24015500000000001 0.197738 +-0.076830499999999996 -0.20389099999999999 0.20217599999999999 +-0.10664999999999999 -0.18507100000000001 0.204627 +-0.048507700000000001 -0.30671799999999999 0.14971999999999999 +-0.10985300000000001 -0.27012399999999998 0.16877500000000001 +0.020159400000000001 -0.26358599999999999 0.11985899999999999 +-0.0820192 -0.303394 0.145977 +-0.11151800000000001 -0.28692499999999999 0.148566 +0.033791700000000001 -0.209923 0.115845 +0.019949499999999998 -0.19298699999999999 0.125832 +0.00080011299999999997 -0.19967099999999999 0.13874900000000001 +0.0102764 -0.16375000000000001 0.13344700000000001 +0.015325999999999999 -0.12934699999999999 0.14213100000000001 +0.043303899999999999 -0.239647 0.099889199999999997 +-0.010328800000000001 -0.10643 0.14183100000000001 +-0.032521000000000001 -0.10838299999999999 0.13683899999999999 +-0.085985000000000006 -0.101282 0.15809300000000001 +-0.108849 -0.11043600000000001 0.16941700000000001 +-0.052558500000000001 -0.097836599999999996 0.13975499999999999 +-0.10316599999999999 -0.15746599999999999 0.19631499999999999 +-0.112763 -0.133107 0.183753 +-0.063495300000000005 -0.15489 0.17952499999999999 +-0.064211199999999996 -0.12604799999999999 0.16286 +-0.086169300000000004 -0.13996600000000001 0.183699 +-0.063495300000000005 -0.15489 0.17952499999999999 +-0.023309900000000001 -0.17152600000000001 0.15354100000000001 +-0.0254547 -0.133829 0.14063300000000001 +-0.079254099999999994 -0.17391000000000001 0.197354 +-0.0458796 -0.210094 0.18626999999999999 +-0.063495300000000005 -0.15489 0.17952499999999999 +-0.0097084900000000002 -0.22793099999999999 0.15407299999999999 +-0.0458796 -0.210094 0.18626999999999999 +-0.0458796 -0.210094 0.18626999999999999 +-0.075232300000000002 -0.238787 0.19677600000000001 +-0.049308200000000003 -0.25528899999999999 0.18343899999999999 +-0.0218198 -0.25775500000000001 0.16445000000000001 +-0.049308200000000003 -0.25528899999999999 0.18343899999999999 +-0.041782300000000001 -0.28198800000000002 0.16825000000000001 +0.00026201499999999999 -0.25824399999999997 0.14286599999999999 +0.015174200000000001 -0.229959 0.128466 +-0.0097084900000000002 -0.22793099999999999 0.15407299999999999 +3 43 0 1 +3 2 3 4 +3 5 6 3 +3 7 5 3 +3 5 7 8 +3 0 43 9 +3 10 2 4 +3 1 44 41 +3 11 45 44 +3 12 9 2 +3 13 12 2 +3 14 15 45 +3 16 45 15 +3 43 1 41 +3 17 16 15 +3 16 31 36 +3 31 16 17 +3 17 18 32 +3 11 19 45 +3 43 2 9 +3 18 20 32 +3 20 21 32 +3 13 2 10 +3 29 28 22 +3 45 19 14 +3 23 29 22 +3 24 22 28 +3 21 24 28 +3 32 21 28 +3 33 29 25 +3 8 33 25 +3 8 7 33 +3 26 29 23 +3 34 33 7 +3 25 29 26 +3 4 3 6 +3 31 17 32 +3 29 27 28 +3 32 30 31 +3 34 35 33 +3 31 37 36 +3 40 38 39 +3 41 42 43 +3 45 46 44 +3 44 46 41 diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_identical_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_identical_test.cpp index ae31ad7f8c8..5e65212e9b4 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_identical_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_identical_test.cpp @@ -11,7 +11,8 @@ typedef CGAL::Exact_predicates_inexact_constructions_kernel K; typedef CGAL::Surface_mesh Surface_mesh; -void test_connected_components(const char* fname) +void test_merge_duplicated_vertices_in_boundary_cycles(const char* fname, + std::size_t expected_nb_vertices) { std::ifstream input(fname); @@ -21,55 +22,22 @@ void test_connected_components(const char* fname) exit(1); } - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + std::cout << "Testing " << fname << "\n"; + std::cout << " input mesh has " << vertices(mesh).size() << " vertices.\n"; + CGAL::Polygon_mesh_processing::merge_duplicated_vertices_in_boundary_cycles(mesh); + std::cout << " output mesh has " << vertices(mesh).size() << " vertices.\n"; - - std::vector > connected_components; - - CGAL::Polygon_mesh_processing::extract_connected_components(mesh, - std::back_inserter(connected_components)); - - std::cout << "# cc = " << connected_components.size(); - - for(auto set : connected_components) - { - std::cout << "of size= " << set.size() << std::endl; - } + assert(expected_nb_vertices==0 || + expected_nb_vertices == vertices(mesh).size()); } -void test_merge_points(const char* fname) +int main(int argc, char** argv) { - std::ifstream input(fname); - - Surface_mesh mesh; - if (!input || !(input >> mesh) || mesh.is_empty()) { - std::cerr << fname << " is not a valid off file.\n"; - exit(1); - } - - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - std::vector verts(vertices(mesh).begin(), vertices(mesh).end()); - - vertex_descriptor v_rm = verts[1]; - vertex_descriptor v_keep = verts[2]; - - CGAL::Polygon_mesh_processing::merge_identical_points(mesh, v_keep, v_rm); - - std::ofstream out("/tmp/result.off"); - out << mesh; - out.close(); -} - - -int main() -{ - - - test_connected_components("data/small_ex.off"); - test_merge_points("data/merge_points.off"); - - - + if (argc==1) + test_merge_duplicated_vertices_in_boundary_cycles("data/merge_points.off", 43); + else + for (int i=1; i< argc; ++i) + test_merge_duplicated_vertices_in_boundary_cycles(argv[i], 0); return 0; } From e1f0740b533159e4510aaa1e8a8c76bd1e4d0dd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 13 Apr 2018 14:00:55 +0200 Subject: [PATCH 09/36] rename header and test file --- .../{stitch_holes.h => merge_border_vertices.h} | 6 +++--- .../test/Polygon_mesh_processing/CMakeLists.txt | 2 +- ..._identical_test.cpp => test_merging_border_vertices.cpp} | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/{stitch_holes.h => merge_border_vertices.h} (96%) rename Polygon_mesh_processing/test/Polygon_mesh_processing/{remove_identical_test.cpp => test_merging_border_vertices.cpp} (95%) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_holes.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h similarity index 96% rename from Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_holes.h rename to Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h index eb2010ed104..7ff0c1ce6e1 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/stitch_holes.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h @@ -19,8 +19,8 @@ // // Author(s) : Sebastien Loriot -#ifndef CGAL_STITCH_HOLES_H -#define CGAL_STITCH_HOLES_H +#ifndef CGAL_POLYGON_MESH_PROCESSING_MERGE_BORDER_VERTICES_H +#define CGAL_POLYGON_MESH_PROCESSING_MERGE_BORDER_VERTICES_H #include #include @@ -168,4 +168,4 @@ void merge_duplicated_vertices_in_boundary_cycles(PolygonMesh& pm) } } // end of CGAL::Polygon_mesh_processing -#endif //CGAL_STITCH_HOLES_H +#endif //CGAL_POLYGON_MESH_PROCESSING_MERGE_BORDER_VERTICES_H diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt index 5ab90c954a9..ee3d82c4fe8 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt @@ -101,7 +101,7 @@ endif() create_single_source_cgal_program("test_orient_cc.cpp") create_single_source_cgal_program("test_pmp_transform.cpp") create_single_source_cgal_program("remove_degeneracies_test.cpp") - create_single_source_cgal_program("remove_identical_test.cpp") + create_single_source_cgal_program("test_merging_border_vertices.cpp") if( TBB_FOUND ) CGAL_target_use_TBB(test_pmp_distance) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_identical_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp similarity index 95% rename from Polygon_mesh_processing/test/Polygon_mesh_processing/remove_identical_test.cpp rename to Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp index 5e65212e9b4..fc749d88373 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_identical_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp @@ -1,7 +1,7 @@ #include #include -#include +#include #include #include From 0830c7a112f2af0a622014aa7073ebfdfd42e1f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 13 Apr 2018 14:09:50 +0200 Subject: [PATCH 10/36] add missing overload --- .../CGAL/Polygon_mesh_processing/merge_border_vertices.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h index 7ff0c1ce6e1..8b78ff256e5 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h @@ -166,6 +166,14 @@ void merge_duplicated_vertices_in_boundary_cycles(PolygonMesh& pm) merge_duplicated_vertices_in_boundary_cycles(pm, parameters::all_default()); } +template +void merge_duplicated_vertices_in_boundary_cycle( + typename boost::graph_traits::halfedge_descriptor h, + PolygonMesh& pm) +{ + merge_duplicated_vertices_in_boundary_cycles(h, pm, parameters::all_default()); +} + } } // end of CGAL::Polygon_mesh_processing #endif //CGAL_POLYGON_MESH_PROCESSING_MERGE_BORDER_VERTICES_H From fe407a701fa6d149abaf90b8c9b16a26f9abca4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 13 Apr 2018 14:54:29 +0200 Subject: [PATCH 11/36] add a function to merge vertices globally --- .../merge_border_vertices.h | 113 +++++++++++++----- .../test_merging_border_vertices.cpp | 44 ++++++- 2 files changed, 126 insertions(+), 31 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h index 8b78ff256e5..76a0d132cca 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h @@ -35,6 +35,46 @@ namespace CGAL{ namespace Polygon_mesh_processing{ +namespace internal { + +// warning: vertices will be altered (sorted) +template +void detect_identical_vertices(std::vector& vertices, + std::vector< std::vector >& identical_vertices, + Vpm vpm) +{ + typedef typename boost::property_traits::value_type Point_3; + + // sort vertices using their point to ease the detection + // of vertices with identical points + CGAL::Property_map_to_unary_function Get_point(vpm); + std::sort( vertices.begin(), vertices.end(), + boost::bind(std::less(), boost::bind(Get_point,_1), + boost::bind(Get_point, _2)) ); + std::size_t nbv=vertices.size(); + std::size_t i=1; + + while(i!=nbv) + { + if (get(vpm, vertices[i]) == get(vpm, vertices[i-1])) + { + identical_vertices.push_back( std::vector() ); + identical_vertices.back().push_back(vertices[i-1]); + identical_vertices.back().push_back(vertices[i]); + while(++i!=nbv) + { + if (get(vpm, vertices[i]) == get(vpm, vertices[i-1])) + identical_vertices.back().push_back(vertices[i]); + else + break; + } + } + ++i; + } +} + +} // end of internal + /// \todo document me /// It should probably go into BGL package template @@ -60,8 +100,8 @@ extract_boundary_cycles(PolygonMesh& pm, /// \ingroup PMP_repairing_grp /// \todo document me template -void merge_vertices(const VertexRange& vertices, - PolygonMesh& pm) +void merge_boundary_vertices(const VertexRange& vertices, + PolygonMesh& pm) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; @@ -102,7 +142,7 @@ void merge_duplicated_vertices_in_boundary_cycle(typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef typename GetVertexPointMap::const_type Vpm; - typedef typename boost::property_traits::value_type Point_3; + Vpm vpm = choose_param(get_param(np, internal_np::vertex_point), get_const_property_map(vertex_point, pm)); @@ -114,35 +154,11 @@ void merge_duplicated_vertices_in_boundary_cycle(typename boost::graph_traits Get_point(vpm); - std::sort( vertices.begin(), vertices.end(), - boost::bind(std::less(), boost::bind(Get_point,_1), - boost::bind(Get_point, _2)) ); - std::size_t nbv=vertices.size(); - std::size_t i=1; - std::vector< std::vector > identical_vertices; - while(i!=nbv) - { - if (get(vpm, vertices[i]) == get(vpm, vertices[i-1])) - { - identical_vertices.push_back( std::vector() ); - identical_vertices.back().push_back(vertices[i-1]); - identical_vertices.back().push_back(vertices[i]); - while(++i!=nbv) - { - if (get(vpm, vertices[i]) == get(vpm, vertices[i-1])) - identical_vertices.back().push_back(vertices[i]); - else - break; - } - } - ++i; - } + internal::detect_identical_vertices(vertices, identical_vertices, vpm); + BOOST_FOREACH(const std::vector& vrtcs, identical_vertices) - merge_vertices(vrtcs, pm); + merge_boundary_vertices(vrtcs, pm); } /// \ingroup PMP_repairing_grp @@ -160,6 +176,36 @@ void merge_duplicated_vertices_in_boundary_cycles( PolygonMesh& pm, merge_duplicated_vertices_in_boundary_cycle(h, pm, np); } + +/// \ingroup PMP_repairing_grp +/// \todo document me +template +void merge_duplicated_boundary_vertices( PolygonMesh& pm, + const NamedParameter& np) +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename GetVertexPointMap::const_type Vpm; + + Vpm vpm = choose_param(get_param(np, internal_np::vertex_point), + get_const_property_map(vertex_point, pm)); + + std::vector border_vertices; + BOOST_FOREACH(halfedge_descriptor h, halfedges(pm)) + { + if(is_border(h, pm)) + border_vertices.push_back(target(h, pm)); + } + + std::vector< std::vector > identical_vertices; + internal::detect_identical_vertices(border_vertices, identical_vertices, vpm); + + BOOST_FOREACH(const std::vector& vrtcs, identical_vertices) + merge_boundary_vertices(vrtcs, pm); +} + + + template void merge_duplicated_vertices_in_boundary_cycles(PolygonMesh& pm) { @@ -174,6 +220,13 @@ void merge_duplicated_vertices_in_boundary_cycle( merge_duplicated_vertices_in_boundary_cycles(h, pm, parameters::all_default()); } +template +void merge_duplicated_boundary_vertices(PolygonMesh& pm) +{ + merge_duplicated_boundary_vertices(pm, parameters::all_default()); +} + + } } // end of CGAL::Polygon_mesh_processing #endif //CGAL_POLYGON_MESH_PROCESSING_MERGE_BORDER_VERTICES_H diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp index fc749d88373..1abc453207a 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp @@ -22,22 +22,64 @@ void test_merge_duplicated_vertices_in_boundary_cycles(const char* fname, exit(1); } - std::cout << "Testing " << fname << "\n"; + std::cout << "Testing merging in cycles " << fname << "\n"; std::cout << " input mesh has " << vertices(mesh).size() << " vertices.\n"; CGAL::Polygon_mesh_processing::merge_duplicated_vertices_in_boundary_cycles(mesh); std::cout << " output mesh has " << vertices(mesh).size() << " vertices.\n"; assert(expected_nb_vertices==0 || expected_nb_vertices == vertices(mesh).size()); + if (expected_nb_vertices==0) + { + std::cout << "writting output to out1.off\n"; + std::ofstream output("out1.off"); + output << std::setprecision(17); + output << mesh; + } +} + +void test_merge_duplicated_boundary_vertices(const char* fname, + std::size_t expected_nb_vertices) +{ + std::ifstream input(fname); + + Surface_mesh mesh; + if (!input || !(input >> mesh) || mesh.is_empty()) { + std::cerr << fname << " is not a valid off file.\n"; + exit(1); + } + + std::cout << "Testing merging globally " << fname << "\n"; + std::cout << " input mesh has " << vertices(mesh).size() << " vertices.\n"; + CGAL::Polygon_mesh_processing::merge_duplicated_boundary_vertices(mesh); + std::cout << " output mesh has " << vertices(mesh).size() << " vertices.\n"; + + assert(expected_nb_vertices == 0 || + expected_nb_vertices == vertices(mesh).size()); + if (expected_nb_vertices==0) + { + std::cout << "writting output to out2.off\n"; + std::ofstream output("out2.off"); + output << std::setprecision(17); + output << mesh; + } } int main(int argc, char** argv) { if (argc==1) + { test_merge_duplicated_vertices_in_boundary_cycles("data/merge_points.off", 43); + test_merge_duplicated_boundary_vertices("data/merge_points.off", 40); + } else + { for (int i=1; i< argc; ++i) + { test_merge_duplicated_vertices_in_boundary_cycles(argv[i], 0); + test_merge_duplicated_boundary_vertices(argv[i], 0); + } + } return 0; } From e6ffc5f505ea2d58e5eef4a972e2523b6c85c519 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 13 Apr 2018 16:25:09 +0200 Subject: [PATCH 12/36] remove incorrect optimisation --- .../CGAL/Polygon_mesh_processing/merge_border_vertices.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h index 76a0d132cca..3435e5fd7bd 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h @@ -66,10 +66,14 @@ void detect_identical_vertices(std::vector& vertices, if (get(vpm, vertices[i]) == get(vpm, vertices[i-1])) identical_vertices.back().push_back(vertices[i]); else + { + ++i; break; + } } } - ++i; + else + ++i; } } From 903df8106a9f7e3f3bbf24be8d371366fed43223 Mon Sep 17 00:00:00 2001 From: Konstantinos Katrioplas Date: Mon, 16 Apr 2018 12:05:03 +0200 Subject: [PATCH 13/36] corrections after the review --- BGL/include/CGAL/boost/graph/helpers.h | 27 --- .../CGAL/Polygon_mesh_processing/repair.h | 195 +++++++++++------- .../data/non_manifold_vertex_duplicated.off | 20 ++ .../remove_degeneracies_test.cpp | 69 ++++--- 4 files changed, 190 insertions(+), 121 deletions(-) create mode 100644 Polygon_mesh_processing/test/Polygon_mesh_processing/data/non_manifold_vertex_duplicated.off diff --git a/BGL/include/CGAL/boost/graph/helpers.h b/BGL/include/CGAL/boost/graph/helpers.h index f34e24b7687..d84e1b5c6c3 100644 --- a/BGL/include/CGAL/boost/graph/helpers.h +++ b/BGL/include/CGAL/boost/graph/helpers.h @@ -973,33 +973,6 @@ make_tetrahedron(const P& p0, const P& p1, const P& p2, const P& p3, Graph& g) return opposite(h2,g); } -/// \cond SKIP_IN_DOC -template -bool is_degenerate_triangle_face( - typename boost::graph_traits::halfedge_descriptor hd, - TriangleMesh& tmesh, - const VertexPointMap& vpmap, - const Traits& traits) -{ - CGAL_assertion(!is_border(hd, tmesh)); - - const typename Traits::Point_3& p1 = get(vpmap, target( hd, tmesh) ); - const typename Traits::Point_3& p2 = get(vpmap, target(next(hd, tmesh), tmesh) ); - const typename Traits::Point_3& p3 = get(vpmap, source( hd, tmesh) ); - return traits.collinear_3_object()(p1, p2, p3); -} - -template -bool is_degenerate_triangle_face( - typename boost::graph_traits::face_descriptor fd, - TriangleMesh& tmesh, - const VertexPointMap& vpmap, - const Traits& traits) -{ - return is_degenerate_triangle_face(halfedge(fd,tmesh), tmesh, vpmap, traits); -} -/// \endcond - /** * \ingroup PkgBGLHelperFct * \brief Creates a triangulated regular prism, outward oriented, diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h index d13eff11f9f..27ea6f5d191 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h @@ -187,46 +187,46 @@ degenerate_faces(const TriangleMesh& tm, OutputIterator out) /// \ingroup PMP_repairing_grp /// checks whether a vertex is non-manifold. /// -/// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph` +/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph` /// /// @param v the vertex to check whether is degenerate -/// @param tm the triangulated surface mesh upon evaluation +/// @param tm triangle mesh containing v /// /// \return true if the vertrex is non-manifold -template -bool is_non_manifold_vertex(typename boost::graph_traits::vertex_descriptor v, - const TriangleMesh& tm) +template +bool is_non_manifold_vertex(typename boost::graph_traits::vertex_descriptor v, + const PolygonMesh& tm) { CGAL_assertion(CGAL::is_triangle_mesh(tm)); - typedef boost::graph_traits GT; + typedef boost::graph_traits GT; typedef typename GT::halfedge_descriptor halfedge_descriptor; boost::unordered_set halfedges_handled; - halfedge_descriptor start = halfedge(v, tm); - halfedge_descriptor h=start; - do{ - halfedges_handled.insert(h); - h=opposite(next(h, tm), tm); - }while(h != start); BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(v, tm)) + halfedges_handled.insert(h); + + BOOST_FOREACH(halfedge_descriptor h, halfedges(tm)) { - if(!halfedges_handled.count(h)) - return true; + if(v == target(h, tm)) + { + if(halfedges_handled.count(h) == 0) + return true; + } } return false; } /// \ingroup PMP_repairing_grp /// checks whether an edge is degenerate. -/// An edge is considered degenerate if two of its vertices share the same location. +/// An edge is considered degenerate if the points of its vertices are identical. /// -/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph` +/// @tparam PolygonMesh a model of `HalfedgeGraph` /// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" /// /// @param e the edge to check whether is degenerate -/// @param pm the triangulated surface mesh upon evaluation +/// @param pm polygon mesh containing e /// @param np optional \ref pmp_namedparameters "Named Parameters" described below /// /// \cgalNamedParamsBegin @@ -258,6 +258,7 @@ bool is_degenerate_edge(typename boost::graph_traits::edge_descript if ( traits.equal_3_object()(get(vpmap, target(e, pm)), get(vpmap, source(e, pm))) ) return true; + return false; } template @@ -267,21 +268,48 @@ bool is_degenerate_edge(typename boost::graph_traits::edge_descript return is_degenerate_edge(e, pm, parameters::all_default()); } +/// \cond SKIP_IN_DOC +template +bool is_degenerate_triangle_face( + typename boost::graph_traits::halfedge_descriptor hd, + TriangleMesh& tmesh, + const VertexPointMap& vpmap, + const Traits& traits) +{ + CGAL_assertion(!is_border(hd, tmesh)); + + const typename Traits::Point_3& p1 = get(vpmap, target( hd, tmesh) ); + const typename Traits::Point_3& p2 = get(vpmap, target(next(hd, tmesh), tmesh) ); + const typename Traits::Point_3& p3 = get(vpmap, source( hd, tmesh) ); + return traits.collinear_3_object()(p1, p2, p3); +} + +template +bool is_degenerate_triangle_face( + typename boost::graph_traits::face_descriptor fd, + TriangleMesh& tmesh, + const VertexPointMap& vpmap, + const Traits& traits) +{ + return is_degenerate_triangle_face(halfedge(fd,tmesh), tmesh, vpmap, traits); +} +/// \endcond + /// \ingroup PMP_repairing_grp /// checks whether a triangle face is degenerate. -/// A triangle face is considered degenerate if all three points of the face are collinear. +/// A triangle face is degenerate if its points are collinear. /// -/// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph` +/// @tparam TriangleMesh a model of `FaceGraph` /// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" /// /// @param f the face to check whether is degenerate -/// @param tm the triangulated surface mesh upon evaluation +/// @param tm triangle mesh containing f /// @param np optional \ref pmp_namedparameters "Named Parameters" described below /// /// \cgalNamedParamsBegin /// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. /// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` should be available in `PolygonMesh` +/// `CGAL::vertex_point_t` should be available in `TriangleMesh` /// \cgalParamEnd /// \cgalParamBegin{geom_traits} a geometric traits class instance. /// The traits class must provide the nested type `Point_3`, @@ -319,26 +347,27 @@ bool is_degenerate_triangle_face(typename boost::graph_traits::fac } /// \ingroup PMP_repairing_grp -/// checks whether a triangle face is needle-like. -/// In a needle-like triangle its longest edge is much longer than the shortest one. +/// checks whether a triangle face is needle. +/// A triangle is needle if its longest edge is much longer than the shortest one. /// -/// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph` +/// @tparam TriangleMesh a model of `FaceGraph` /// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" /// -/// @param f the face to check whether is almost degenerate -/// @param tm the triangulated surface mesh upon evaluation +/// @param f a face to check whether is almost degenerate +/// @param tm triangle mesh containing f /// @param threshold a number in the range [0, 1] to indicate the tolerance /// upon which to characterize the degeneracy. 1 means that needle triangles -/// are those that have a infinitely small edge, while 0 means that needle triangles -/// are those that would have an infinitely long edge +/// are those that have a infinitely small edge, while 0 means that all +/// triangles are considered needles. /// @param np optional \ref pmp_namedparameters "Named Parameters" described below /// /// \cgalNamedParamsBegin /// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. /// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` should be available in `PolygonMesh` +/// `CGAL::vertex_point_t` should be available in `TriangleMesh` /// \cgalParamEnd /// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// The traits class must provide the nested type `Point_3`. /// \cgalParamEnd /// \cgalNamedParamsEnd /// @@ -357,30 +386,58 @@ bool is_needle_triangle_face(typename boost::graph_traits::face_de typedef typename GetVertexPointMap::const_type VertexPointMap; VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), get_const_property_map(vertex_point, tm)); - typedef typename GetGeomTraits::type FT; + typedef typename GetGeomTraits::type Traits; + typedef typename Traits::FT FT; typedef boost::graph_traits GT; - typedef typename GT::halfedge_descriptor halfedge_descriptor; typedef typename GT::vertex_descriptor vertex_descriptor; - typedef typename boost::property_traits::value_type Point; - typedef typename Kernel_traits::Kernel::Vector_3 Vector; + typedef typename boost::property_traits::reference Point_ref; - BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(halfedge(f, tm), tm)) + vertex_descriptor v0 = target(halfedge(f, tm), tm); + vertex_descriptor v1 = target(next(halfedge(f, tm), tm), tm); + vertex_descriptor v2 = target(next(next(halfedge(f, tm), tm), tm), tm); + Point_ref p0 = get(vpmap, v0); + Point_ref p1 = get(vpmap, v1); + Point_ref p2 = get(vpmap, v2); + + // e1 = p0p1 e2 = p1p2 e3 = p2p3 + FT e1 = CGAL::squared_distance(p0,p1); + FT e2 = CGAL::squared_distance(p1,p2); + FT e3 = CGAL::squared_distance(p2,p0); + + FT smallest, largest; + if(e1 < e2) { - vertex_descriptor v0 = source(h, tm); - vertex_descriptor v1 = target(h, tm); - vertex_descriptor v2 = target(next(h, tm), tm); - - Vector a = get(vpmap, v0) - get(vpmap, v1); - Vector b = get(vpmap, v2) - get(vpmap, v1); - double ab = a*b; - double aa = a.squared_length(); - double bb = b.squared_length(); - double dot_ab = a*b / (CGAL::sqrt(aa) * CGAL::sqrt(bb)); - - // threshold = 1 means no tolerance, totally degenerate - if(dot_ab > threshold) - return true; + if(e1 < e3) + smallest = e1; + else + smallest = e3; } + else + { + if(e2 < e3) + smallest = e2; + else + smallest = e3; + } + if(e1 > e2) + { + if(e1 > e3) + largest = e1; + else + largest = e3; + } + else + { + if(e2 > e3) + largest = e2; + else + largest = e3; + } + + const double ratio = smallest / largest; + // threshold is opposite + if(ratio < (1 - threshold)) + return true; return false; } @@ -389,30 +446,31 @@ bool is_needle_triangle_face(typename boost::graph_traits::face_de const TriangleMesh& tm, const double threshold) { - is_needle_triangle_face(f, tm, threshold, parameters::all_default()); + return is_needle_triangle_face(f, tm, threshold, parameters::all_default()); } /// \ingroup PMP_repairing_grp -/// checks whether a triangle face is cap-like. -/// A cap-like triangle has an angle very close to 180 degrees. +/// checks whether a triangle face is a cap. +/// A triangle is a cap if it has an angle very close to 180 degrees. /// -/// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph` +/// @tparam TriangleMesh a model of `FaceGraph` /// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" /// /// @param f the face to check whether is almost degenerate -/// @param tm the triangulated surface mesh upon evaluation +/// @param tm triangle mesh containing f /// @param threshold a number in the range [0, 1] to indicate the tolerance /// upon which to characterize the degeneracy. 1 means that cap triangles -/// are considered those whose vertices for an angle of 180 degrees, while 0 means that +/// are considered those whose vertices form an angle of 180 degrees, while 0 means that /// all triangles are considered caps. /// @param np optional \ref pmp_namedparameters "Named Parameters" described below /// /// \cgalNamedParamsBegin /// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. /// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` should be available in `PolygonMesh` +/// `CGAL::vertex_point_t` should be available in `TriangleMesh` /// \cgalParamEnd /// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// The traits class must provide the nested type `Point_3` /// \cgalParamEnd /// \cgalNamedParamsEnd /// @@ -431,28 +489,27 @@ bool is_cap_triangle_face(typename boost::graph_traits::face_descr typedef typename GetVertexPointMap::const_type VertexPointMap; VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), get_const_property_map(vertex_point, tm)); - typedef typename GetGeomTraits::type FT; + typedef typename GetGeomTraits::type Traits; + typedef typename Traits::FT FT; typedef boost::graph_traits GT; typedef typename GT::halfedge_descriptor halfedge_descriptor; typedef typename GT::vertex_descriptor vertex_descriptor; - typedef typename boost::property_traits::value_type Point; - typedef typename Kernel_traits::Kernel::Vector_3 Vector; + typedef typename boost::property_traits::value_type Point_type; + typedef typename Kernel_traits::Kernel::Vector_3 Vector; BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(halfedge(f, tm), tm)) { vertex_descriptor v0 = source(h, tm); vertex_descriptor v1 = target(h, tm); vertex_descriptor v2 = target(next(h, tm), tm); - - Vector a = get(vpmap, v0) - get(vpmap, v1); + Vector a = get(vpmap, v0) - get (vpmap, v1); Vector b = get(vpmap, v2) - get(vpmap, v1); - double ab = a*b; - double aa = a.squared_length(); - double bb = b.squared_length(); - double dot_ab = a*b / (CGAL::sqrt(aa) * CGAL::sqrt(bb)); + FT aa = a.squared_length(); + FT bb = b.squared_length(); + FT dot_ab = (a*b) / (aa * bb); // threshold = 1 means no tolerance, totally degenerate - // take the opposite, because cos it -1 at 180 degrees + // take the opposite, because cos is -1 at 180 degrees if(dot_ab < -threshold) return true; } @@ -464,7 +521,7 @@ bool is_cap_triangle_face(typename boost::graph_traits::face_descr const TriangleMesh& tm, const double threshold) { - is_cap_triangle_face(f, tm, threshold, parameters::all_default()); + return is_cap_triangle_face(f, tm, threshold, parameters::all_default()); } // this function remove a border edge even if it does not satisfy the link condition. @@ -1013,7 +1070,7 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh, // Then, remove triangles made of 3 collinear points std::set degenerate_face_set; BOOST_FOREACH(face_descriptor fd, faces(tmesh)) - if ( is_degenerate_triangle_face(fd, tmesh) ) + if ( is_degenerate_triangle_face(fd, tmesh, np)) degenerate_face_set.insert(fd); nb_deg_faces+=degenerate_face_set.size(); @@ -1044,7 +1101,7 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh, degenerate_face_set.erase( face(hd2, tmesh) ); // remove the central vertex and check if the new face is degenerated hd=CGAL::Euler::remove_center_vertex(hd, tmesh); - if (is_degenerate_triangle_face(face(hd, tmesh), tmesh)) + if (is_degenerate_triangle_face(face(hd, tmesh), tmesh, np)) { degenerate_face_set.insert( face(hd, tmesh) ); } @@ -1572,7 +1629,7 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh) /// \ingroup PMP_repairing_grp /// duplicates all non-manifold vertices of the input mesh. /// -/// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph` +/// @tparam TriangleMesh a model of `HalfedgeListGraph` and `MutableHalfedgeGraph` /// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" /// /// @param tm the triangulated surface mesh to be repaired diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/data/non_manifold_vertex_duplicated.off b/Polygon_mesh_processing/test/Polygon_mesh_processing/data/non_manifold_vertex_duplicated.off new file mode 100644 index 00000000000..5733b99f480 --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/data/non_manifold_vertex_duplicated.off @@ -0,0 +1,20 @@ +OFF +8 8 0 +0 1 0 +1 0 0 +0 0 0 +0 0 1 +2 1 0 +2 0 0 +2 0 -1 +1 0 0 +3 0 1 2 +3 2 3 0 +3 1 3 2 +3 0 3 1 +3 7 5 4 +3 7 6 5 +3 4 6 7 +3 5 6 4 + + diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp index 82c275c9702..e3e737e1a63 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include @@ -39,9 +39,12 @@ void check_edge_degeneracy(const char* fname) std::cerr << fname << " is not a valid off file.\n"; exit(1); } + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + std::vector all_edges(edges(mesh).begin(), edges(mesh).end()); - BOOST_FOREACH(typename boost::graph_traits::edge_descriptor e, edges(mesh)) - CGAL::Polygon_mesh_processing::is_degenerate_edge(e, mesh); + CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[0], mesh)); + CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[1], mesh)); + CGAL_assertion(CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[2], mesh)); } void check_triangle_face_degeneracy(const char* fname) @@ -54,80 +57,96 @@ void check_triangle_face_degeneracy(const char* fname) exit(1); } - BOOST_FOREACH(typename boost::graph_traits::face_descriptor f, faces(mesh)) - CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, mesh); + typedef typename boost::graph_traits::face_descriptor face_descriptor; + std::vector all_faces(faces(mesh).begin(), faces(mesh).end()); + CGAL_assertion(CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[0], mesh)); + CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[1], mesh)); + CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[2], mesh)); + CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[3], mesh)); } -void test_vetices_duplication(const char* fname) +void test_vertices_merge_and_duplication(const char* fname) { std::ifstream input(fname); - Surface_mesh mesh; if (!input || !(input >> mesh) || mesh.is_empty()) { std::cerr << fname << " is not a valid off file.\n"; exit(1); } + const std::size_t initial_vertices = vertices(mesh).size(); + + // create non-manifold vertex + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + std::vector all_vertices(vertices(mesh).begin(), vertices(mesh).end()); + CGAL::Polygon_mesh_processing::merge_identical_points(mesh, all_vertices[1], all_vertices[7]); + + const std::size_t vertices_after_merge = vertices(mesh).size(); + CGAL_assertion(vertices_after_merge == initial_vertices - 1); CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices(mesh); + const std::size_t final_vertices = vertices(mesh).size(); + CGAL_assertion(final_vertices == vertices_after_merge + 1); + CGAL_assertion(final_vertices == initial_vertices); } void test_vertex_non_manifoldness(const char* fname) { std::ifstream input(fname); - Surface_mesh mesh; if (!input || !(input >> mesh) || mesh.is_empty()) { std::cerr << fname << " is not a valid off file.\n"; exit(1); } - BOOST_FOREACH(typename boost::graph_traits::vertex_descriptor v, vertices(mesh)) - { - if(CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh)) - std::cout << "true\n"; + // create non-manifold vertex + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + std::vector all_vertices(vertices(mesh).begin(), vertices(mesh).end()); + CGAL::Polygon_mesh_processing::merge_identical_points(mesh, all_vertices[1], all_vertices[7]); + std::vector vertices_with_non_manifold(vertices(mesh).begin(), vertices(mesh).end()); + CGAL_assertion(vertices_with_non_manifold.size() == all_vertices.size() - 1); + BOOST_FOREACH(std::size_t iv, vertices(mesh)) + { + vertex_descriptor v = vertices_with_non_manifold[iv]; + if(iv == 1) + CGAL_assertion(CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh)); + else + CGAL_assertion(!CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh)); } } void test_needle(const char* fname) { std::ifstream input(fname); - Surface_mesh mesh; if (!input || !(input >> mesh) || mesh.is_empty()) { std::cerr << fname << " is not a valid off file.\n"; exit(1); } - double threshold = 0.8; - + const double threshold = 0.8; BOOST_FOREACH(typename boost::graph_traits::face_descriptor f, faces(mesh)) { - if(CGAL::Polygon_mesh_processing::is_needle_triangle_face(f, mesh, threshold)) - std::cout << "needle\n"; + CGAL_assertion(CGAL::Polygon_mesh_processing::is_needle_triangle_face(f, mesh, threshold)); } } void test_cap(const char* fname) { std::ifstream input(fname); - Surface_mesh mesh; if (!input || !(input >> mesh) || mesh.is_empty()) { std::cerr << fname << " is not a valid off file.\n"; exit(1); } - double threshold = 0.8; - + const double threshold = 0.8; BOOST_FOREACH(typename boost::graph_traits::face_descriptor f, faces(mesh)) { - if(CGAL::Polygon_mesh_processing::is_cap_triangle_face(f, mesh, threshold)) - std::cout << "cap\n"; + CGAL_assertion(CGAL::Polygon_mesh_processing::is_cap_triangle_face(f, mesh, threshold)); } } - int main() { fix("data_degeneracies/degtri_2dt_1edge_split_twice.off"); @@ -139,8 +158,8 @@ int main() fix("data_degeneracies/trihole.off"); check_edge_degeneracy("data_degeneracies/degtri_edge.off"); check_triangle_face_degeneracy("data_degeneracies/degtri_four.off"); - test_vetices_duplication("data_degeneracies/degtri_four.off"); - test_vertex_non_manifoldness("data/non_manifold_vertex.off"); + test_vertices_merge_and_duplication("data_degeneracies/non_manifold_vertex_duplicated.off"); + test_vertex_non_manifoldness("data_degeneracies/non_manifold_vertex_duplicated.off"); test_needle("data_degeneracies/needle.off"); test_cap("data_degeneracies/cap.off"); From 63f49b7fcc45456c222e0ba6944b265dd2e0228c Mon Sep 17 00:00:00 2001 From: Konstantinos Katrioplas Date: Tue, 17 Apr 2018 11:14:41 +0200 Subject: [PATCH 14/36] move predicates to helper.h and seperate test file --- .../CGAL/Polygon_mesh_processing/helpers.h | 407 ++++++++++++++++++ .../CGAL/Polygon_mesh_processing/repair.h | 344 +-------------- .../Polygon_mesh_processing/CMakeLists.txt | 1 + .../remove_degeneracies_test.cpp | 123 ------ .../test_predicates.cpp | 139 ++++++ 5 files changed, 550 insertions(+), 464 deletions(-) create mode 100644 Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h create mode 100644 Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h new file mode 100644 index 00000000000..20c35e54d13 --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h @@ -0,0 +1,407 @@ +// Copyright (c) 2015 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0+ +// +// +// Author(s) : Konstantinos Katrioplas + +#ifndef CGAL_POLYGON_MESH_PROCESSING_HELPERS_H +#define CGAL_POLYGON_MESH_PROCESSING_HELPERS_H + +#include +#include + + +namespace CGAL { + +namespace Polygon_mesh_processing { + +namespace internal { + +template +void merge_identical_points(PolygonMesh& mesh, + typename boost::graph_traits::vertex_descriptor v_keep, + typename boost::graph_traits::vertex_descriptor v_rm) +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + halfedge_descriptor h = halfedge(v_rm, mesh); + halfedge_descriptor start = h; + + do{ + set_target(h, v_keep, mesh); + h = opposite(next(h, mesh), mesh); + } while( h != start ); + + remove_vertex(v_rm, mesh); +} +} // end internal + + + +/// \ingroup PMP_repairing_grp +/// checks whether a vertex is non-manifold. +/// +/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph` +/// +/// @param v the vertex to check whether is degenerate +/// @param tm triangle mesh containing v +/// +/// \return true if the vertrex is non-manifold +template +bool is_non_manifold_vertex(typename boost::graph_traits::vertex_descriptor v, + const PolygonMesh& tm) +{ + CGAL_assertion(CGAL::is_triangle_mesh(tm)); + + typedef boost::graph_traits GT; + typedef typename GT::halfedge_descriptor halfedge_descriptor; + + boost::unordered_set halfedges_handled; + + BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(v, tm)) + halfedges_handled.insert(h); + + BOOST_FOREACH(halfedge_descriptor h, halfedges(tm)) + { + if(v == target(h, tm)) + { + if(halfedges_handled.count(h) == 0) + return true; + } + } + return false; +} + +/// \ingroup PMP_repairing_grp +/// checks whether an edge is degenerate. +/// An edge is considered degenerate if the points of its vertices are identical. +/// +/// @tparam PolygonMesh a model of `HalfedgeGraph` +/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" +/// +/// @param e the edge to check whether is degenerate +/// @param pm polygon mesh containing e +/// @param np optional \ref pmp_namedparameters "Named Parameters" described below +/// +/// \cgalNamedParamsBegin +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `PolygonMesh` +/// \cgalParamEnd +/// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// The traits class must provide the nested type `Point_3`, +/// and the nested functor : +/// - `Equal_3` to check whether 2 points are identical +/// \cgalParamEnd +/// \cgalNamedParamsEnd +/// +/// \return true if the edge is degenerate +template +bool is_degenerate_edge(typename boost::graph_traits::edge_descriptor e, + const PolygonMesh& pm, + const NamedParameters& np) +{ + using boost::get_param; + using boost::choose_param; + + typedef typename GetVertexPointMap::const_type VertexPointMap; + VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), + get_const_property_map(vertex_point, pm)); + typedef typename GetGeomTraits::type Traits; + Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits()); + + if ( traits.equal_3_object()(get(vpmap, target(e, pm)), get(vpmap, source(e, pm))) ) + return true; + return false; +} + +template +bool is_degenerate_edge(typename boost::graph_traits::edge_descriptor e, + const PolygonMesh& pm) +{ + return is_degenerate_edge(e, pm, parameters::all_default()); +} + +/// \cond SKIP_IN_DOC +template +bool is_degenerate_triangle_face( + typename boost::graph_traits::halfedge_descriptor hd, + TriangleMesh& tmesh, + const VertexPointMap& vpmap, + const Traits& traits) +{ + CGAL_assertion(!is_border(hd, tmesh)); + + const typename Traits::Point_3& p1 = get(vpmap, target( hd, tmesh) ); + const typename Traits::Point_3& p2 = get(vpmap, target(next(hd, tmesh), tmesh) ); + const typename Traits::Point_3& p3 = get(vpmap, source( hd, tmesh) ); + return traits.collinear_3_object()(p1, p2, p3); +} + +template +bool is_degenerate_triangle_face( + typename boost::graph_traits::face_descriptor fd, + TriangleMesh& tmesh, + const VertexPointMap& vpmap, + const Traits& traits) +{ + return is_degenerate_triangle_face(halfedge(fd,tmesh), tmesh, vpmap, traits); +} +/// \endcond + +/// \ingroup PMP_repairing_grp +/// checks whether a triangle face is degenerate. +/// A triangle face is degenerate if its points are collinear. +/// +/// @tparam TriangleMesh a model of `FaceGraph` +/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" +/// +/// @param f the face to check whether is degenerate +/// @param tm triangle mesh containing f +/// @param np optional \ref pmp_namedparameters "Named Parameters" described below +/// +/// \cgalNamedParamsBegin +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `TriangleMesh` +/// \cgalParamEnd +/// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// The traits class must provide the nested type `Point_3`, +/// and the nested functor : +/// - `Collinear_3` to check whether 3 points are collinear +/// \cgalParamEnd +/// \cgalNamedParamsEnd +/// +/// \return true if the triangle face is degenerate +template +bool is_degenerate_triangle_face(typename boost::graph_traits::face_descriptor f, + const TriangleMesh& tm, + const NamedParameters& np) +{ + CGAL_assertion(CGAL::is_triangle_mesh(tm)); + + using boost::get_param; + using boost::choose_param; + + typedef typename GetVertexPointMap::const_type VertexPointMap; + VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), + get_const_property_map(vertex_point, tm)); + typedef typename GetGeomTraits::type Traits; + Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits()); + + typename boost::graph_traits::halfedge_descriptor hd = halfedge(f,tm); + const typename Traits::Point_3& p1 = get(vpmap, target( hd, tm) ); + const typename Traits::Point_3& p2 = get(vpmap, target(next(hd, tm), tm) ); + const typename Traits::Point_3& p3 = get(vpmap, source( hd, tm) ); + return traits.collinear_3_object()(p1, p2, p3); + +} + +template +bool is_degenerate_triangle_face(typename boost::graph_traits::face_descriptor f, + const TriangleMesh& tm) +{ + return CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, tm, parameters::all_default()); +} + +/// \ingroup PMP_repairing_grp +/// checks whether a triangle face is needle. +/// A triangle is needle if its longest edge is much longer than the shortest one. +/// +/// @tparam TriangleMesh a model of `FaceGraph` +/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" +/// +/// @param f a face to check whether is almost degenerate +/// @param tm triangle mesh containing f +/// @param threshold a number in the range [0, 1] to indicate the tolerance +/// upon which to characterize the degeneracy. 1 means that needle triangles +/// are those that have a infinitely small edge, while 0 means that all +/// triangles are considered needles. +/// @param np optional \ref pmp_namedparameters "Named Parameters" described below +/// +/// \cgalNamedParamsBegin +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `TriangleMesh` +/// \cgalParamEnd +/// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// The traits class must provide the nested type `Point_3`. +/// \cgalParamEnd +/// \cgalNamedParamsEnd +/// +/// \return true if the triangle face is almost degenerate +template +bool is_needle_triangle_face(typename boost::graph_traits::face_descriptor f, + const TriangleMesh& tm, + const double threshold, + const NamedParameters& np) +{ + CGAL_assertion(CGAL::is_triangle_mesh(tm)); + + using boost::get_param; + using boost::choose_param; + + typedef typename GetVertexPointMap::const_type VertexPointMap; + VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), + get_const_property_map(vertex_point, tm)); + typedef typename GetGeomTraits::type Traits; + typedef typename Traits::FT FT; + typedef boost::graph_traits GT; + typedef typename GT::vertex_descriptor vertex_descriptor; + typedef typename boost::property_traits::reference Point_ref; + + vertex_descriptor v0 = target(halfedge(f, tm), tm); + vertex_descriptor v1 = target(next(halfedge(f, tm), tm), tm); + vertex_descriptor v2 = target(next(next(halfedge(f, tm), tm), tm), tm); + Point_ref p0 = get(vpmap, v0); + Point_ref p1 = get(vpmap, v1); + Point_ref p2 = get(vpmap, v2); + + // e1 = p0p1 e2 = p1p2 e3 = p2p3 + FT e1 = CGAL::squared_distance(p0,p1); + FT e2 = CGAL::squared_distance(p1,p2); + FT e3 = CGAL::squared_distance(p2,p0); + + FT smallest, largest; + if(e1 < e2) + { + if(e1 < e3) + smallest = e1; + else + smallest = e3; + } + else + { + if(e2 < e3) + smallest = e2; + else + smallest = e3; + } + if(e1 > e2) + { + if(e1 > e3) + largest = e1; + else + largest = e3; + } + else + { + if(e2 > e3) + largest = e2; + else + largest = e3; + } + + const double ratio = smallest / largest; + // threshold is opposite + if(ratio < (1 - threshold)) + return true; + return false; +} + +template +bool is_needle_triangle_face(typename boost::graph_traits::face_descriptor f, + const TriangleMesh& tm, + const double threshold) +{ + return is_needle_triangle_face(f, tm, threshold, parameters::all_default()); +} + +/// \ingroup PMP_repairing_grp +/// checks whether a triangle face is a cap. +/// A triangle is a cap if it has an angle very close to 180 degrees. +/// +/// @tparam TriangleMesh a model of `FaceGraph` +/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" +/// +/// @param f the face to check whether is almost degenerate +/// @param tm triangle mesh containing f +/// @param threshold a number in the range [0, 1] to indicate the tolerance +/// upon which to characterize the degeneracy. 1 means that cap triangles +/// are considered those whose vertices form an angle of 180 degrees, while 0 means that +/// all triangles are considered caps. +/// @param np optional \ref pmp_namedparameters "Named Parameters" described below +/// +/// \cgalNamedParamsBegin +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `TriangleMesh` +/// \cgalParamEnd +/// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// The traits class must provide the nested type `Point_3` +/// \cgalParamEnd +/// \cgalNamedParamsEnd +/// +/// \return true if the triangle face is almost degenerate +template +bool is_cap_triangle_face(typename boost::graph_traits::face_descriptor f, + const TriangleMesh& tm, + const double threshold, + const NamedParameters& np) +{ + CGAL_assertion(CGAL::is_triangle_mesh(tm)); + + using boost::get_param; + using boost::choose_param; + + typedef typename GetVertexPointMap::const_type VertexPointMap; + VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), + get_const_property_map(vertex_point, tm)); + typedef typename GetGeomTraits::type Traits; + typedef typename Traits::FT FT; + typedef boost::graph_traits GT; + typedef typename GT::halfedge_descriptor halfedge_descriptor; + typedef typename GT::vertex_descriptor vertex_descriptor; + typedef typename boost::property_traits::value_type Point_type; + typedef typename Kernel_traits::Kernel::Vector_3 Vector; + + BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(halfedge(f, tm), tm)) + { + vertex_descriptor v0 = source(h, tm); + vertex_descriptor v1 = target(h, tm); + vertex_descriptor v2 = target(next(h, tm), tm); + Vector a = get(vpmap, v0) - get (vpmap, v1); + Vector b = get(vpmap, v2) - get(vpmap, v1); + FT aa = a.squared_length(); + FT bb = b.squared_length(); + FT dot_ab = (a*b) / (aa * bb); + + // threshold = 1 means no tolerance, totally degenerate + // take the opposite, because cos is -1 at 180 degrees + if(dot_ab < -threshold) + return true; + } + return false; +} + +template +bool is_cap_triangle_face(typename boost::graph_traits::face_descriptor f, + const TriangleMesh& tm, + const double threshold) +{ + return is_cap_triangle_face(f, tm, threshold, parameters::all_default()); +} + + + + +} } // end namespaces CGAL and PMP + + + +#endif // CGAL_POLYGON_MESH_PROCESSING_HELPERS_H + diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h index 27ea6f5d191..8352c191f47 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h @@ -40,6 +40,7 @@ #include #include +#include #include #include @@ -157,6 +158,7 @@ struct Less_vertex_point{ } }; +// to be removed template OutputIterator degenerate_faces(const TriangleMesh& tm, @@ -167,7 +169,7 @@ degenerate_faces(const TriangleMesh& tm, typedef typename boost::graph_traits::face_descriptor face_descriptor; BOOST_FOREACH(face_descriptor fd, faces(tm)) { - if ( is_degenerate_triangle_face(fd, tm, vpmap, traits) ) + if ( is_degenerate_triangle_face(fd, tm) ) *out++=fd; } return out; @@ -184,346 +186,6 @@ degenerate_faces(const TriangleMesh& tm, OutputIterator out) return degenerate_faces(tm, get(vertex_point, tm), Kernel(), out); } -/// \ingroup PMP_repairing_grp -/// checks whether a vertex is non-manifold. -/// -/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph` -/// -/// @param v the vertex to check whether is degenerate -/// @param tm triangle mesh containing v -/// -/// \return true if the vertrex is non-manifold -template -bool is_non_manifold_vertex(typename boost::graph_traits::vertex_descriptor v, - const PolygonMesh& tm) -{ - CGAL_assertion(CGAL::is_triangle_mesh(tm)); - - typedef boost::graph_traits GT; - typedef typename GT::halfedge_descriptor halfedge_descriptor; - - boost::unordered_set halfedges_handled; - - BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(v, tm)) - halfedges_handled.insert(h); - - BOOST_FOREACH(halfedge_descriptor h, halfedges(tm)) - { - if(v == target(h, tm)) - { - if(halfedges_handled.count(h) == 0) - return true; - } - } - return false; -} - -/// \ingroup PMP_repairing_grp -/// checks whether an edge is degenerate. -/// An edge is considered degenerate if the points of its vertices are identical. -/// -/// @tparam PolygonMesh a model of `HalfedgeGraph` -/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" -/// -/// @param e the edge to check whether is degenerate -/// @param pm polygon mesh containing e -/// @param np optional \ref pmp_namedparameters "Named Parameters" described below -/// -/// \cgalNamedParamsBegin -/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. -/// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` should be available in `PolygonMesh` -/// \cgalParamEnd -/// \cgalParamBegin{geom_traits} a geometric traits class instance. -/// The traits class must provide the nested type `Point_3`, -/// and the nested functor : -/// - `Equal_3` to check whether 2 points are identical -/// \cgalParamEnd -/// \cgalNamedParamsEnd -/// -/// \return true if the edge is degenerate -template -bool is_degenerate_edge(typename boost::graph_traits::edge_descriptor e, - const PolygonMesh& pm, - const NamedParameters& np) -{ - using boost::get_param; - using boost::choose_param; - - typedef typename GetVertexPointMap::const_type VertexPointMap; - VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), - get_const_property_map(vertex_point, pm)); - typedef typename GetGeomTraits::type Traits; - Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits()); - - if ( traits.equal_3_object()(get(vpmap, target(e, pm)), get(vpmap, source(e, pm))) ) - return true; - return false; -} - -template -bool is_degenerate_edge(typename boost::graph_traits::edge_descriptor e, - const PolygonMesh& pm) -{ - return is_degenerate_edge(e, pm, parameters::all_default()); -} - -/// \cond SKIP_IN_DOC -template -bool is_degenerate_triangle_face( - typename boost::graph_traits::halfedge_descriptor hd, - TriangleMesh& tmesh, - const VertexPointMap& vpmap, - const Traits& traits) -{ - CGAL_assertion(!is_border(hd, tmesh)); - - const typename Traits::Point_3& p1 = get(vpmap, target( hd, tmesh) ); - const typename Traits::Point_3& p2 = get(vpmap, target(next(hd, tmesh), tmesh) ); - const typename Traits::Point_3& p3 = get(vpmap, source( hd, tmesh) ); - return traits.collinear_3_object()(p1, p2, p3); -} - -template -bool is_degenerate_triangle_face( - typename boost::graph_traits::face_descriptor fd, - TriangleMesh& tmesh, - const VertexPointMap& vpmap, - const Traits& traits) -{ - return is_degenerate_triangle_face(halfedge(fd,tmesh), tmesh, vpmap, traits); -} -/// \endcond - -/// \ingroup PMP_repairing_grp -/// checks whether a triangle face is degenerate. -/// A triangle face is degenerate if its points are collinear. -/// -/// @tparam TriangleMesh a model of `FaceGraph` -/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" -/// -/// @param f the face to check whether is degenerate -/// @param tm triangle mesh containing f -/// @param np optional \ref pmp_namedparameters "Named Parameters" described below -/// -/// \cgalNamedParamsBegin -/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. -/// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` should be available in `TriangleMesh` -/// \cgalParamEnd -/// \cgalParamBegin{geom_traits} a geometric traits class instance. -/// The traits class must provide the nested type `Point_3`, -/// and the nested functor : -/// - `Collinear_3` to check whether 3 points are collinear -/// \cgalParamEnd -/// \cgalNamedParamsEnd -/// -/// \return true if the triangle face is degenerate -template -bool is_degenerate_triangle_face(typename boost::graph_traits::face_descriptor f, - const TriangleMesh& tm, - const NamedParameters& np) -{ - CGAL_assertion(CGAL::is_triangle_mesh(tm)); - - using boost::get_param; - using boost::choose_param; - - typedef typename GetVertexPointMap::const_type VertexPointMap; - VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), - get_const_property_map(vertex_point, tm)); - typedef typename GetGeomTraits::type Traits; - Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits()); - - // call from BGL helpers - return is_degenerate_triangle_face(f, tm, vpmap, traits); -} - -template -bool is_degenerate_triangle_face(typename boost::graph_traits::face_descriptor f, - const TriangleMesh& tm) -{ - return CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, tm, parameters::all_default()); -} - -/// \ingroup PMP_repairing_grp -/// checks whether a triangle face is needle. -/// A triangle is needle if its longest edge is much longer than the shortest one. -/// -/// @tparam TriangleMesh a model of `FaceGraph` -/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" -/// -/// @param f a face to check whether is almost degenerate -/// @param tm triangle mesh containing f -/// @param threshold a number in the range [0, 1] to indicate the tolerance -/// upon which to characterize the degeneracy. 1 means that needle triangles -/// are those that have a infinitely small edge, while 0 means that all -/// triangles are considered needles. -/// @param np optional \ref pmp_namedparameters "Named Parameters" described below -/// -/// \cgalNamedParamsBegin -/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. -/// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` should be available in `TriangleMesh` -/// \cgalParamEnd -/// \cgalParamBegin{geom_traits} a geometric traits class instance. -/// The traits class must provide the nested type `Point_3`. -/// \cgalParamEnd -/// \cgalNamedParamsEnd -/// -/// \return true if the triangle face is almost degenerate -template -bool is_needle_triangle_face(typename boost::graph_traits::face_descriptor f, - const TriangleMesh& tm, - const double threshold, - const NamedParameters& np) -{ - CGAL_assertion(CGAL::is_triangle_mesh(tm)); - - using boost::get_param; - using boost::choose_param; - - typedef typename GetVertexPointMap::const_type VertexPointMap; - VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), - get_const_property_map(vertex_point, tm)); - typedef typename GetGeomTraits::type Traits; - typedef typename Traits::FT FT; - typedef boost::graph_traits GT; - typedef typename GT::vertex_descriptor vertex_descriptor; - typedef typename boost::property_traits::reference Point_ref; - - vertex_descriptor v0 = target(halfedge(f, tm), tm); - vertex_descriptor v1 = target(next(halfedge(f, tm), tm), tm); - vertex_descriptor v2 = target(next(next(halfedge(f, tm), tm), tm), tm); - Point_ref p0 = get(vpmap, v0); - Point_ref p1 = get(vpmap, v1); - Point_ref p2 = get(vpmap, v2); - - // e1 = p0p1 e2 = p1p2 e3 = p2p3 - FT e1 = CGAL::squared_distance(p0,p1); - FT e2 = CGAL::squared_distance(p1,p2); - FT e3 = CGAL::squared_distance(p2,p0); - - FT smallest, largest; - if(e1 < e2) - { - if(e1 < e3) - smallest = e1; - else - smallest = e3; - } - else - { - if(e2 < e3) - smallest = e2; - else - smallest = e3; - } - if(e1 > e2) - { - if(e1 > e3) - largest = e1; - else - largest = e3; - } - else - { - if(e2 > e3) - largest = e2; - else - largest = e3; - } - - const double ratio = smallest / largest; - // threshold is opposite - if(ratio < (1 - threshold)) - return true; - return false; -} - -template -bool is_needle_triangle_face(typename boost::graph_traits::face_descriptor f, - const TriangleMesh& tm, - const double threshold) -{ - return is_needle_triangle_face(f, tm, threshold, parameters::all_default()); -} - -/// \ingroup PMP_repairing_grp -/// checks whether a triangle face is a cap. -/// A triangle is a cap if it has an angle very close to 180 degrees. -/// -/// @tparam TriangleMesh a model of `FaceGraph` -/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" -/// -/// @param f the face to check whether is almost degenerate -/// @param tm triangle mesh containing f -/// @param threshold a number in the range [0, 1] to indicate the tolerance -/// upon which to characterize the degeneracy. 1 means that cap triangles -/// are considered those whose vertices form an angle of 180 degrees, while 0 means that -/// all triangles are considered caps. -/// @param np optional \ref pmp_namedparameters "Named Parameters" described below -/// -/// \cgalNamedParamsBegin -/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. -/// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` should be available in `TriangleMesh` -/// \cgalParamEnd -/// \cgalParamBegin{geom_traits} a geometric traits class instance. -/// The traits class must provide the nested type `Point_3` -/// \cgalParamEnd -/// \cgalNamedParamsEnd -/// -/// \return true if the triangle face is almost degenerate -template -bool is_cap_triangle_face(typename boost::graph_traits::face_descriptor f, - const TriangleMesh& tm, - const double threshold, - const NamedParameters& np) -{ - CGAL_assertion(CGAL::is_triangle_mesh(tm)); - - using boost::get_param; - using boost::choose_param; - - typedef typename GetVertexPointMap::const_type VertexPointMap; - VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), - get_const_property_map(vertex_point, tm)); - typedef typename GetGeomTraits::type Traits; - typedef typename Traits::FT FT; - typedef boost::graph_traits GT; - typedef typename GT::halfedge_descriptor halfedge_descriptor; - typedef typename GT::vertex_descriptor vertex_descriptor; - typedef typename boost::property_traits::value_type Point_type; - typedef typename Kernel_traits::Kernel::Vector_3 Vector; - - BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(halfedge(f, tm), tm)) - { - vertex_descriptor v0 = source(h, tm); - vertex_descriptor v1 = target(h, tm); - vertex_descriptor v2 = target(next(h, tm), tm); - Vector a = get(vpmap, v0) - get (vpmap, v1); - Vector b = get(vpmap, v2) - get(vpmap, v1); - FT aa = a.squared_length(); - FT bb = b.squared_length(); - FT dot_ab = (a*b) / (aa * bb); - - // threshold = 1 means no tolerance, totally degenerate - // take the opposite, because cos is -1 at 180 degrees - if(dot_ab < -threshold) - return true; - } - return false; -} - -template -bool is_cap_triangle_face(typename boost::graph_traits::face_descriptor f, - const TriangleMesh& tm, - const double threshold) -{ - return is_cap_triangle_face(f, tm, threshold, parameters::all_default()); -} - // this function remove a border edge even if it does not satisfy the link condition. // The only limitation is that the length connected component of the boundary this edge // is strictly greater than 3 diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt index ee3d82c4fe8..0c26363fca7 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt @@ -102,6 +102,7 @@ endif() create_single_source_cgal_program("test_pmp_transform.cpp") create_single_source_cgal_program("remove_degeneracies_test.cpp") create_single_source_cgal_program("test_merging_border_vertices.cpp") + create_single_source_cgal_program("test_predicates.cpp") if( TBB_FOUND ) CGAL_target_use_TBB(test_pmp_distance) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp index e3e737e1a63..8b6488320e1 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -30,122 +29,6 @@ void fix(const char* fname) assert( CGAL::is_valid_polygon_mesh(mesh) ); } -void check_edge_degeneracy(const char* fname) -{ - std::ifstream input(fname); - - Surface_mesh mesh; - if (!input || !(input >> mesh) || mesh.is_empty()) { - std::cerr << fname << " is not a valid off file.\n"; - exit(1); - } - typedef typename boost::graph_traits::edge_descriptor edge_descriptor; - std::vector all_edges(edges(mesh).begin(), edges(mesh).end()); - - CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[0], mesh)); - CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[1], mesh)); - CGAL_assertion(CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[2], mesh)); -} - -void check_triangle_face_degeneracy(const char* fname) -{ - std::ifstream input(fname); - - Surface_mesh mesh; - if (!input || !(input >> mesh) || mesh.is_empty()) { - std::cerr << fname << " is not a valid off file.\n"; - exit(1); - } - - typedef typename boost::graph_traits::face_descriptor face_descriptor; - std::vector all_faces(faces(mesh).begin(), faces(mesh).end()); - CGAL_assertion(CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[0], mesh)); - CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[1], mesh)); - CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[2], mesh)); - CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[3], mesh)); -} - -void test_vertices_merge_and_duplication(const char* fname) -{ - std::ifstream input(fname); - Surface_mesh mesh; - if (!input || !(input >> mesh) || mesh.is_empty()) { - std::cerr << fname << " is not a valid off file.\n"; - exit(1); - } - const std::size_t initial_vertices = vertices(mesh).size(); - - // create non-manifold vertex - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - std::vector all_vertices(vertices(mesh).begin(), vertices(mesh).end()); - CGAL::Polygon_mesh_processing::merge_identical_points(mesh, all_vertices[1], all_vertices[7]); - - const std::size_t vertices_after_merge = vertices(mesh).size(); - CGAL_assertion(vertices_after_merge == initial_vertices - 1); - - CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices(mesh); - const std::size_t final_vertices = vertices(mesh).size(); - CGAL_assertion(final_vertices == vertices_after_merge + 1); - CGAL_assertion(final_vertices == initial_vertices); -} - -void test_vertex_non_manifoldness(const char* fname) -{ - std::ifstream input(fname); - Surface_mesh mesh; - if (!input || !(input >> mesh) || mesh.is_empty()) { - std::cerr << fname << " is not a valid off file.\n"; - exit(1); - } - - // create non-manifold vertex - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - std::vector all_vertices(vertices(mesh).begin(), vertices(mesh).end()); - CGAL::Polygon_mesh_processing::merge_identical_points(mesh, all_vertices[1], all_vertices[7]); - std::vector vertices_with_non_manifold(vertices(mesh).begin(), vertices(mesh).end()); - CGAL_assertion(vertices_with_non_manifold.size() == all_vertices.size() - 1); - - BOOST_FOREACH(std::size_t iv, vertices(mesh)) - { - vertex_descriptor v = vertices_with_non_manifold[iv]; - if(iv == 1) - CGAL_assertion(CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh)); - else - CGAL_assertion(!CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh)); - } -} - -void test_needle(const char* fname) -{ - std::ifstream input(fname); - Surface_mesh mesh; - if (!input || !(input >> mesh) || mesh.is_empty()) { - std::cerr << fname << " is not a valid off file.\n"; - exit(1); - } - - const double threshold = 0.8; - BOOST_FOREACH(typename boost::graph_traits::face_descriptor f, faces(mesh)) - { - CGAL_assertion(CGAL::Polygon_mesh_processing::is_needle_triangle_face(f, mesh, threshold)); - } -} - -void test_cap(const char* fname) -{ - std::ifstream input(fname); - Surface_mesh mesh; - if (!input || !(input >> mesh) || mesh.is_empty()) { - std::cerr << fname << " is not a valid off file.\n"; - exit(1); - } - - const double threshold = 0.8; - BOOST_FOREACH(typename boost::graph_traits::face_descriptor f, faces(mesh)) - { - CGAL_assertion(CGAL::Polygon_mesh_processing::is_cap_triangle_face(f, mesh, threshold)); - } -} int main() { @@ -156,12 +39,6 @@ int main() fix("data_degeneracies/degtri_three.off"); fix("data_degeneracies/degtri_single.off"); fix("data_degeneracies/trihole.off"); - check_edge_degeneracy("data_degeneracies/degtri_edge.off"); - check_triangle_face_degeneracy("data_degeneracies/degtri_four.off"); - test_vertices_merge_and_duplication("data_degeneracies/non_manifold_vertex_duplicated.off"); - test_vertex_non_manifoldness("data_degeneracies/non_manifold_vertex_duplicated.off"); - test_needle("data_degeneracies/needle.off"); - test_cap("data_degeneracies/cap.off"); return 0; } diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp new file mode 100644 index 00000000000..9d1ed0dea26 --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp @@ -0,0 +1,139 @@ +#include +#include +#include +#include +#include +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Surface_mesh; + +void check_edge_degeneracy(const char* fname) +{ + std::ifstream input(fname); + + Surface_mesh mesh; + if (!input || !(input >> mesh) || mesh.is_empty()) { + std::cerr << fname << " is not a valid off file.\n"; + exit(1); + } + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + std::vector all_edges(edges(mesh).begin(), edges(mesh).end()); + + CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[0], mesh)); + CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[1], mesh)); + CGAL_assertion(CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[2], mesh)); +} + +void check_triangle_face_degeneracy(const char* fname) +{ + std::ifstream input(fname); + + Surface_mesh mesh; + if (!input || !(input >> mesh) || mesh.is_empty()) { + std::cerr << fname << " is not a valid off file.\n"; + exit(1); + } + + typedef typename boost::graph_traits::face_descriptor face_descriptor; + std::vector all_faces(faces(mesh).begin(), faces(mesh).end()); + CGAL_assertion(CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[0], mesh)); + CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[1], mesh)); + CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[2], mesh)); + CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[3], mesh)); +} + +// temp left here: tests repair.h +void test_vertices_merge_and_duplication(const char* fname) +{ + std::ifstream input(fname); + Surface_mesh mesh; + if (!input || !(input >> mesh) || mesh.is_empty()) { + std::cerr << fname << " is not a valid off file.\n"; + exit(1); + } + const std::size_t initial_vertices = vertices(mesh).size(); + + // create non-manifold vertex + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + std::vector all_vertices(vertices(mesh).begin(), vertices(mesh).end()); + CGAL::Polygon_mesh_processing::internal::merge_identical_points(mesh, all_vertices[1], all_vertices[7]); + + const std::size_t vertices_after_merge = vertices(mesh).size(); + CGAL_assertion(vertices_after_merge == initial_vertices - 1); + + CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices(mesh); + const std::size_t final_vertices = vertices(mesh).size(); + CGAL_assertion(final_vertices == vertices_after_merge + 1); + CGAL_assertion(final_vertices == initial_vertices); +} + +void test_vertex_non_manifoldness(const char* fname) +{ + std::ifstream input(fname); + Surface_mesh mesh; + if (!input || !(input >> mesh) || mesh.is_empty()) { + std::cerr << fname << " is not a valid off file.\n"; + exit(1); + } + + // create non-manifold vertex + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + std::vector all_vertices(vertices(mesh).begin(), vertices(mesh).end()); + CGAL::Polygon_mesh_processing::internal::merge_identical_points(mesh, all_vertices[1], all_vertices[7]); + std::vector vertices_with_non_manifold(vertices(mesh).begin(), vertices(mesh).end()); + CGAL_assertion(vertices_with_non_manifold.size() == all_vertices.size() - 1); + + BOOST_FOREACH(std::size_t iv, vertices(mesh)) + { + vertex_descriptor v = vertices_with_non_manifold[iv]; + if(iv == 1) + CGAL_assertion(CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh)); + else + CGAL_assertion(!CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh)); + } +} + +void test_needle(const char* fname) +{ + std::ifstream input(fname); + Surface_mesh mesh; + if (!input || !(input >> mesh) || mesh.is_empty()) { + std::cerr << fname << " is not a valid off file.\n"; + exit(1); + } + + const double threshold = 0.8; + BOOST_FOREACH(typename boost::graph_traits::face_descriptor f, faces(mesh)) + { + CGAL_assertion(CGAL::Polygon_mesh_processing::is_needle_triangle_face(f, mesh, threshold)); + } +} + +void test_cap(const char* fname) +{ + std::ifstream input(fname); + Surface_mesh mesh; + if (!input || !(input >> mesh) || mesh.is_empty()) { + std::cerr << fname << " is not a valid off file.\n"; + exit(1); + } + + const double threshold = 0.8; + BOOST_FOREACH(typename boost::graph_traits::face_descriptor f, faces(mesh)) + { + CGAL_assertion(CGAL::Polygon_mesh_processing::is_cap_triangle_face(f, mesh, threshold)); + } +} + +int main() +{ + check_edge_degeneracy("data_degeneracies/degtri_edge.off"); + check_triangle_face_degeneracy("data_degeneracies/degtri_four.off"); + test_vertices_merge_and_duplication("data_degeneracies/non_manifold_vertex_duplicated.off"); + test_vertex_non_manifoldness("data_degeneracies/non_manifold_vertex_duplicated.off"); + test_needle("data_degeneracies/needle.off"); + test_cap("data_degeneracies/cap.off"); + + return 0; +} From 71041e03769ae7dc749488a5d8f0778877b33469 Mon Sep 17 00:00:00 2001 From: Konstantinos Katrioplas Date: Tue, 17 Apr 2018 12:41:17 +0200 Subject: [PATCH 15/36] replace is_degenerate_triangle_face predicate with new version from PMP helpers --- .../CGAL/Polygon_mesh_processing/helpers.h | 27 ------------------- .../Isotropic_remeshing/remesh_impl.h | 13 ++++----- .../CGAL/Polygon_mesh_processing/repair.h | 19 ++----------- .../Plugins/PMP/Degenerated_faces_plugin.cpp | 3 ++- .../Plugins/PMP/Selection_plugin.cpp | 7 +++-- .../Edit_polyhedron_plugin.cpp | 6 ++--- .../demo/Polyhedron/Scene_polyhedron_item.cpp | 3 ++- .../Scene_polyhedron_selection_item.cpp | 3 ++- .../Polyhedron/Scene_surface_mesh_item.cpp | 3 ++- .../include/CGAL/statistics_helpers.h | 3 ++- 10 files changed, 24 insertions(+), 63 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h index 20c35e54d13..24174b778d9 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h @@ -136,33 +136,6 @@ bool is_degenerate_edge(typename boost::graph_traits::edge_descript return is_degenerate_edge(e, pm, parameters::all_default()); } -/// \cond SKIP_IN_DOC -template -bool is_degenerate_triangle_face( - typename boost::graph_traits::halfedge_descriptor hd, - TriangleMesh& tmesh, - const VertexPointMap& vpmap, - const Traits& traits) -{ - CGAL_assertion(!is_border(hd, tmesh)); - - const typename Traits::Point_3& p1 = get(vpmap, target( hd, tmesh) ); - const typename Traits::Point_3& p2 = get(vpmap, target(next(hd, tmesh), tmesh) ); - const typename Traits::Point_3& p3 = get(vpmap, source( hd, tmesh) ); - return traits.collinear_3_object()(p1, p2, p3); -} - -template -bool is_degenerate_triangle_face( - typename boost::graph_traits::face_descriptor fd, - TriangleMesh& tmesh, - const VertexPointMap& vpmap, - const Traits& traits) -{ - return is_degenerate_triangle_face(halfedge(fd,tmesh), tmesh, vpmap, traits); -} -/// \endcond - /// \ingroup PMP_repairing_grp /// checks whether a triangle face is degenerate. /// A triangle face is degenerate if its points are collinear. diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index bfbe181b40e..9686f4c271b 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -364,7 +365,7 @@ namespace internal { BOOST_FOREACH(face_descriptor f, face_range) { - if (is_degenerate_triangle_face(halfedge(f,mesh_),mesh_,vpmap_,GeomTraits())){ + if (is_degenerate_triangle_face(f, mesh_,)){ continue; } Patch_id pid = get_patch_id(f); @@ -1593,7 +1594,7 @@ private: { if (is_border(h, mesh_)) continue; - if (is_degenerate_triangle_face(h, mesh_, vpmap_, GeomTraits())) + if (is_degenerate_triangle_face(face(h), mesh_)) degenerate_faces.insert(h); } while(!degenerate_faces.empty()) @@ -1601,7 +1602,7 @@ private: halfedge_descriptor h = *(degenerate_faces.begin()); degenerate_faces.erase(degenerate_faces.begin()); - if (!is_degenerate_triangle_face(h, mesh_, vpmap_, GeomTraits())) + if (!is_degenerate_triangle_face(face(h), mesh_)) //this can happen when flipping h has consequences further in the mesh continue; @@ -1654,10 +1655,10 @@ private: } if (!is_border(hf, mesh_) - && is_degenerate_triangle_face(hf, mesh_, vpmap_, GeomTraits())) + && is_degenerate_triangle_face(face(h), mesh_)) degenerate_faces.insert(hf); if (!is_border(hfo, mesh_) - && is_degenerate_triangle_face(hfo, mesh_, vpmap_, GeomTraits())) + && is_degenerate_triangle_face(face(h), mesh_)) degenerate_faces.insert(hfo); break; @@ -1676,7 +1677,7 @@ private: { if (is_border(h, mesh_)) continue; - if (is_degenerate_triangle_face(h, mesh_, vpmap_, GeomTraits())) + if (is_degenerate_triangle_face(face(h), mesh_)) return true; } return false; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h index 8352c191f47..cced686125d 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h @@ -158,13 +158,9 @@ struct Less_vertex_point{ } }; -// to be removed -template +template OutputIterator -degenerate_faces(const TriangleMesh& tm, - const VertexPointMap& vpmap, - const Traits& traits, - OutputIterator out) +degenerate_faces(const TriangleMesh& tm, OutputIterator out) { typedef typename boost::graph_traits::face_descriptor face_descriptor; BOOST_FOREACH(face_descriptor fd, faces(tm)) @@ -175,17 +171,6 @@ degenerate_faces(const TriangleMesh& tm, return out; } -template -OutputIterator -degenerate_faces(const TriangleMesh& tm, OutputIterator out) -{ - typedef typename boost::property_map::type Vpm; - typedef typename boost::property_traits::value_type Point; - typedef typename Kernel_traits::Kernel Kernel; - - return degenerate_faces(tm, get(vertex_point, tm), Kernel(), out); -} - // this function remove a border edge even if it does not satisfy the link condition. // The only limitation is that the length connected component of the boundary this edge // is strictly greater than 3 diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Degenerated_faces_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Degenerated_faces_plugin.cpp index 8fd94b78149..b7c359e9ab1 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Degenerated_faces_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Degenerated_faces_plugin.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #ifdef USE_SURFACE_MESH typedef Scene_surface_mesh_item Scene_facegraph_item; #else @@ -87,7 +88,7 @@ bool isDegen(Mesh* mesh, std::vector::face_de BOOST_FOREACH(FaceDescriptor f, faces(*mesh)) { if(is_triangle(halfedge(f, *mesh), *mesh) - && is_degenerate_triangle_face(f, *mesh, get(boost::vertex_point, *mesh), Kernel()) ) + && CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, *mesh) ) out_faces.push_back(f); } return !out_faces.empty(); diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Selection_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Selection_plugin.cpp index a6cf3a63299..ac1bfd837d4 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Selection_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Selection_plugin.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #ifdef USE_SURFACE_MESH @@ -745,10 +746,8 @@ public Q_SLOTS: bool is_valid = true; BOOST_FOREACH(boost::graph_traits::face_descriptor fd, faces(*selection_item->polyhedron())) { - if (CGAL::is_degenerate_triangle_face(fd, - *selection_item->polyhedron(), - vpmap, - CGAL::Kernel_traits< boost::property_traits::value_type >::Kernel())) + if (CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(fd, + *selection_item->polyhedron())) { is_valid = false; break; diff --git a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Edit_polyhedron_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Edit_polyhedron_plugin.cpp index 730d6447313..18a5d1bb8e0 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Edit_polyhedron_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Edit_polyhedron_plugin.cpp @@ -9,6 +9,7 @@ #include "Scene_edit_polyhedron_item.h" #include "Scene_polyhedron_selection_item.h" #include +#include #include #include #include @@ -421,10 +422,7 @@ void Polyhedron_demo_edit_polyhedron_plugin::dock_widget_visibility_changed(bool bool is_valid = true; BOOST_FOREACH(boost::graph_traits::face_descriptor fd, faces(*poly_item->face_graph())) { - if (CGAL::is_degenerate_triangle_face(fd, - *poly_item->face_graph(), - get(boost::vertex_point, - *poly_item->face_graph()), Kernel())) + if (CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(fd, *poly_item->face_graph())) { is_valid = false; break; diff --git a/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.cpp b/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.cpp index 58a466c7ad1..266f5331b94 100644 --- a/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -276,7 +277,7 @@ void* Scene_polyhedron_item_priv::get_aabb_tree() int index =0; BOOST_FOREACH( Polyhedron::Facet_iterator f, faces(*poly)) { - if (CGAL::is_degenerate_triangle_face(f, *poly, get(CGAL::vertex_point, *poly), Kernel())) + if (CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, *poly)) continue; if(!f->is_triangle()) { diff --git a/Polyhedron/demo/Polyhedron/Scene_polyhedron_selection_item.cpp b/Polyhedron/demo/Polyhedron/Scene_polyhedron_selection_item.cpp index 6df22ab25e7..488e874d416 100644 --- a/Polyhedron/demo/Polyhedron/Scene_polyhedron_selection_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_polyhedron_selection_item.cpp @@ -2,6 +2,7 @@ #include "Scene_polyhedron_selection_item.h" #include #include +#include #include #include #include @@ -2032,7 +2033,7 @@ bool Scene_polyhedron_selection_item_priv::canAddFace(fg_halfedge_descriptor hc, fg_halfedge_descriptor res = CGAL::Euler::add_face_to_border(t,hc, *item->polyhedron()); - if(CGAL::is_degenerate_triangle_face(res, *item->polyhedron(), get(CGAL::vertex_point, *item->polyhedron()), Kernel())) + if(CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(res, *item->polyhedron())) { CGAL::Euler::remove_face(res, *item->polyhedron()); tempInstructions("Edge not selected : resulting facet is degenerated.", diff --git a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp index 29c31b34c70..75e60907f61 100644 --- a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -1039,7 +1040,7 @@ void* Scene_surface_mesh_item_priv::get_aabb_tree() BOOST_FOREACH( face_descriptor f, faces(*sm)) { //if face is degenerate, skip it - if (CGAL::is_degenerate_triangle_face(f, *sm, get(CGAL::vertex_point, *sm), EPICK())) + if (CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, *sm)) continue; //if face not triangle, triangulate corresponding primitive before adding it to the tree if(!CGAL::is_triangle(halfedge(f, *sm), *sm)) diff --git a/Polyhedron/demo/Polyhedron/include/CGAL/statistics_helpers.h b/Polyhedron/demo/Polyhedron/include/CGAL/statistics_helpers.h index 28799d40fd9..711a641dcc3 100644 --- a/Polyhedron/demo/Polyhedron/include/CGAL/statistics_helpers.h +++ b/Polyhedron/demo/Polyhedron/include/CGAL/statistics_helpers.h @@ -14,6 +14,7 @@ #include #include +#include template @@ -92,7 +93,7 @@ unsigned int nb_degenerate_faces(Mesh* poly, VPmap vpmap) unsigned int nb = 0; BOOST_FOREACH(face_descriptor f, faces(*poly)) { - if (CGAL::is_degenerate_triangle_face(f, *poly, vpmap, Traits())) + if (CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, *poly)) ++nb; } return nb; From c6afed86a3db493a1bef08a65e193b8f9a9f229c Mon Sep 17 00:00:00 2001 From: Konstantinos Katrioplas Date: Tue, 17 Apr 2018 13:31:51 +0200 Subject: [PATCH 16/36] use cosine for threshold on needles and caps --- .../CGAL/Polygon_mesh_processing/helpers.h | 205 ++++++++---------- .../test_predicates.cpp | 2 +- 2 files changed, 88 insertions(+), 119 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h index 24174b778d9..dad4a29831a 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h @@ -50,14 +50,12 @@ void merge_identical_points(PolygonMesh& mesh, } } // end internal - - /// \ingroup PMP_repairing_grp /// checks whether a vertex is non-manifold. /// /// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph` /// -/// @param v the vertex to check whether is degenerate +/// @param v the vertex /// @param tm triangle mesh containing v /// /// \return true if the vertrex is non-manifold @@ -93,7 +91,7 @@ bool is_non_manifold_vertex(typename boost::graph_traits::vertex_de /// @tparam PolygonMesh a model of `HalfedgeGraph` /// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" /// -/// @param e the edge to check whether is degenerate +/// @param e the edge /// @param pm polygon mesh containing e /// @param np optional \ref pmp_namedparameters "Named Parameters" described below /// @@ -143,7 +141,7 @@ bool is_degenerate_edge(typename boost::graph_traits::edge_descript /// @tparam TriangleMesh a model of `FaceGraph` /// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" /// -/// @param f the face to check whether is degenerate +/// @param f the triangle face /// @param tm triangle mesh containing f /// @param np optional \ref pmp_namedparameters "Named Parameters" described below /// @@ -198,12 +196,11 @@ bool is_degenerate_triangle_face(typename boost::graph_traits::fac /// @tparam TriangleMesh a model of `FaceGraph` /// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" /// -/// @param f a face to check whether is almost degenerate +/// @param f the triangle face /// @param tm triangle mesh containing f -/// @param threshold a number in the range [0, 1] to indicate the tolerance -/// upon which to characterize the degeneracy. 1 means that needle triangles -/// are those that have a infinitely small edge, while 0 means that all -/// triangles are considered needles. +/// @param threshold the cosine of an angle of f. +/// The threshold is in range [0 1] and corresponds to +/// angles between 0 and 90 degrees. /// @param np optional \ref pmp_namedparameters "Named Parameters" described below /// /// \cgalNamedParamsBegin @@ -216,7 +213,7 @@ bool is_degenerate_triangle_face(typename boost::graph_traits::fac /// \cgalParamEnd /// \cgalNamedParamsEnd /// -/// \return true if the triangle face is almost degenerate +/// \return true if the triangle face is a needle template bool is_needle_triangle_face(typename boost::graph_traits::face_descriptor f, const TriangleMesh& tm, @@ -224,109 +221,8 @@ bool is_needle_triangle_face(typename boost::graph_traits::face_de const NamedParameters& np) { CGAL_assertion(CGAL::is_triangle_mesh(tm)); - - using boost::get_param; - using boost::choose_param; - - typedef typename GetVertexPointMap::const_type VertexPointMap; - VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), - get_const_property_map(vertex_point, tm)); - typedef typename GetGeomTraits::type Traits; - typedef typename Traits::FT FT; - typedef boost::graph_traits GT; - typedef typename GT::vertex_descriptor vertex_descriptor; - typedef typename boost::property_traits::reference Point_ref; - - vertex_descriptor v0 = target(halfedge(f, tm), tm); - vertex_descriptor v1 = target(next(halfedge(f, tm), tm), tm); - vertex_descriptor v2 = target(next(next(halfedge(f, tm), tm), tm), tm); - Point_ref p0 = get(vpmap, v0); - Point_ref p1 = get(vpmap, v1); - Point_ref p2 = get(vpmap, v2); - - // e1 = p0p1 e2 = p1p2 e3 = p2p3 - FT e1 = CGAL::squared_distance(p0,p1); - FT e2 = CGAL::squared_distance(p1,p2); - FT e3 = CGAL::squared_distance(p2,p0); - - FT smallest, largest; - if(e1 < e2) - { - if(e1 < e3) - smallest = e1; - else - smallest = e3; - } - else - { - if(e2 < e3) - smallest = e2; - else - smallest = e3; - } - if(e1 > e2) - { - if(e1 > e3) - largest = e1; - else - largest = e3; - } - else - { - if(e2 > e3) - largest = e2; - else - largest = e3; - } - - const double ratio = smallest / largest; - // threshold is opposite - if(ratio < (1 - threshold)) - return true; - return false; -} - -template -bool is_needle_triangle_face(typename boost::graph_traits::face_descriptor f, - const TriangleMesh& tm, - const double threshold) -{ - return is_needle_triangle_face(f, tm, threshold, parameters::all_default()); -} - -/// \ingroup PMP_repairing_grp -/// checks whether a triangle face is a cap. -/// A triangle is a cap if it has an angle very close to 180 degrees. -/// -/// @tparam TriangleMesh a model of `FaceGraph` -/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" -/// -/// @param f the face to check whether is almost degenerate -/// @param tm triangle mesh containing f -/// @param threshold a number in the range [0, 1] to indicate the tolerance -/// upon which to characterize the degeneracy. 1 means that cap triangles -/// are considered those whose vertices form an angle of 180 degrees, while 0 means that -/// all triangles are considered caps. -/// @param np optional \ref pmp_namedparameters "Named Parameters" described below -/// -/// \cgalNamedParamsBegin -/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. -/// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` should be available in `TriangleMesh` -/// \cgalParamEnd -/// \cgalParamBegin{geom_traits} a geometric traits class instance. -/// The traits class must provide the nested type `Point_3` -/// \cgalParamEnd -/// \cgalNamedParamsEnd -/// -/// \return true if the triangle face is almost degenerate -template -bool is_cap_triangle_face(typename boost::graph_traits::face_descriptor f, - const TriangleMesh& tm, - const double threshold, - const NamedParameters& np) -{ - CGAL_assertion(CGAL::is_triangle_mesh(tm)); + CGAL_assertion(threshold >= 0); + CGAL_assertion(threshold <= 1); using boost::get_param; using boost::choose_param; @@ -351,11 +247,84 @@ bool is_cap_triangle_face(typename boost::graph_traits::face_descr Vector b = get(vpmap, v2) - get(vpmap, v1); FT aa = a.squared_length(); FT bb = b.squared_length(); - FT dot_ab = (a*b) / (aa * bb); + FT squared_dot_ab = ((a*b)*(a*b)) / (aa * bb); - // threshold = 1 means no tolerance, totally degenerate - // take the opposite, because cos is -1 at 180 degrees - if(dot_ab < -threshold) + if(squared_dot_ab > threshold * threshold) + return true; + } + return false; + +} + +template +bool is_needle_triangle_face(typename boost::graph_traits::face_descriptor f, + const TriangleMesh& tm, + const double threshold) +{ + return is_needle_triangle_face(f, tm, threshold, parameters::all_default()); +} + +/// \ingroup PMP_repairing_grp +/// checks whether a triangle face is a cap. +/// A triangle is a cap if it has an angle very close to 180 degrees. +/// +/// @tparam TriangleMesh a model of `FaceGraph` +/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" +/// +/// @param f the triangle face +/// @param tm triangle mesh containing f +/// @param threshold the cosine of an angle of f. +/// The threshold is in range [-1 0] and corresponds to +/// angles between 90 and 180 degrees. +/// @param np optional \ref pmp_namedparameters "Named Parameters" described below +/// +/// \cgalNamedParamsBegin +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `TriangleMesh` +/// \cgalParamEnd +/// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// The traits class must provide the nested type `Point_3` +/// \cgalParamEnd +/// \cgalNamedParamsEnd +/// +/// \return true if the triangle face is a cap +template +bool is_cap_triangle_face(typename boost::graph_traits::face_descriptor f, + const TriangleMesh& tm, + const double threshold, + const NamedParameters& np) +{ + CGAL_assertion(CGAL::is_triangle_mesh(tm)); + CGAL_assertion(threshold >= -1); + CGAL_assertion(threshold <= 0); + + using boost::get_param; + using boost::choose_param; + + typedef typename GetVertexPointMap::const_type VertexPointMap; + VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), + get_const_property_map(vertex_point, tm)); + typedef typename GetGeomTraits::type Traits; + typedef typename Traits::FT FT; + typedef boost::graph_traits GT; + typedef typename GT::halfedge_descriptor halfedge_descriptor; + typedef typename GT::vertex_descriptor vertex_descriptor; + typedef typename boost::property_traits::value_type Point_type; + typedef typename Kernel_traits::Kernel::Vector_3 Vector; + + BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(halfedge(f, tm), tm)) + { + vertex_descriptor v0 = source(h, tm); + vertex_descriptor v1 = target(h, tm); + vertex_descriptor v2 = target(next(h, tm), tm); + Vector a = get(vpmap, v0) - get (vpmap, v1); + Vector b = get(vpmap, v2) - get(vpmap, v1); + FT aa = a.squared_length(); + FT bb = b.squared_length(); + FT squared_dot_ab = ((a*b)*(a*b)) / (aa * bb); + + if(squared_dot_ab > threshold * threshold) return true; } return false; diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp index 9d1ed0dea26..679f96762d3 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp @@ -119,7 +119,7 @@ void test_cap(const char* fname) exit(1); } - const double threshold = 0.8; + const double threshold = -0.8; BOOST_FOREACH(typename boost::graph_traits::face_descriptor f, faces(mesh)) { CGAL_assertion(CGAL::Polygon_mesh_processing::is_cap_triangle_face(f, mesh, threshold)); From 032ee2828a29dbc3fa7f4f427e2f35ae10266724 Mon Sep 17 00:00:00 2001 From: Konstantinos Katrioplas Date: Wed, 18 Apr 2018 18:11:47 +0200 Subject: [PATCH 17/36] named parameters for duplicate non-manifold vertices --- .../CGAL/boost/graph/parameters_interface.h | 1 + BGL/test/BGL/test_cgal_bgl_named_params.cpp | 3 ++ .../CGAL/Polygon_mesh_processing/helpers.h | 51 +++++++++++++++++++ .../CGAL/Polygon_mesh_processing/repair.h | 49 ++++++++++++++---- .../test_predicates.cpp | 13 +++-- 5 files changed, 102 insertions(+), 15 deletions(-) diff --git a/BGL/include/CGAL/boost/graph/parameters_interface.h b/BGL/include/CGAL/boost/graph/parameters_interface.h index 16357cb39dd..657972a74a8 100644 --- a/BGL/include/CGAL/boost/graph/parameters_interface.h +++ b/BGL/include/CGAL/boost/graph/parameters_interface.h @@ -70,6 +70,7 @@ CGAL_add_named_parameter(projection_functor_t, projection_functor, projection_fu CGAL_add_named_parameter(throw_on_self_intersection_t, throw_on_self_intersection, throw_on_self_intersection) CGAL_add_named_parameter(clip_volume_t, clip_volume, clip_volume) CGAL_add_named_parameter(use_compact_clipper_t, use_compact_clipper, use_compact_clipper) +CGAL_add_named_parameter(output_iterator_t, output_iterator, output_iterator) // List of named parameters that we use in the package 'Surface Mesh Simplification' CGAL_add_named_parameter(get_cost_policy_t, get_cost_policy, get_cost) diff --git a/BGL/test/BGL/test_cgal_bgl_named_params.cpp b/BGL/test/BGL/test_cgal_bgl_named_params.cpp index 4bb47789fde..c0081450d1e 100644 --- a/BGL/test/BGL/test_cgal_bgl_named_params.cpp +++ b/BGL/test/BGL/test_cgal_bgl_named_params.cpp @@ -92,6 +92,7 @@ void test(const NamedParameters& np) assert(get_param(np, CGAL::internal_np::verbosity_level).v == 41); assert(get_param(np, CGAL::internal_np::projection_functor).v == 42); assert(get_param(np, CGAL::internal_np::apply_per_connected_component).v == 46); + assert(get_param(np, CGAL::internal_np::output_iterator).v == 47); // Test types @@ -162,6 +163,7 @@ void test(const NamedParameters& np) check_same_type<41>(get_param(np, CGAL::internal_np::verbosity_level)); check_same_type<42>(get_param(np, CGAL::internal_np::projection_functor)); check_same_type<46>(get_param(np, CGAL::internal_np::apply_per_connected_component)); + check_same_type472>(get_param(np, CGAL::internal_np::output_iterator)); } int main() @@ -217,6 +219,7 @@ int main() .clip_volume(A<44>(44)) .use_compact_clipper(A<45>(45)) .apply_per_connected_component(A<46>(46)) + .output_iterator(A<47>(47)) ); return EXIT_SUCCESS; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h index dad4a29831a..ea38a91b835 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h @@ -32,6 +32,57 @@ namespace Polygon_mesh_processing { namespace internal { +template +struct No_constraint_pmap +{ +public: + typedef Descriptor key_type; + typedef bool value_type; + typedef value_type& reference; + typedef boost::read_write_property_map_tag category; + + friend bool get(const No_constraint_pmap& , const key_type& ) { + return false; + } + friend void put(No_constraint_pmap& , const key_type& , const bool ) {} +}; + +template +struct Vertex_collector +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + void collect_vertices(vertex_descriptor v1, vertex_descriptor v2) + { + std::vector& verts = collections[v1]; + if (verts.empty()) + verts.push_back(v1); + verts.push_back(v2); + } + + void dump(OutputIterator out) + { + typedef std::pair > Pair_type; + BOOST_FOREACH(const Pair_type& p, collections) + { + *out++=p.second; + } + } + + std::map > collections; +}; + +template +struct Vertex_collector +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + void collect_vertices(vertex_descriptor, vertex_descriptor) + {} + + void dump(Emptyset_iterator) + {} +}; + +// used only for testing template void merge_identical_points(PolygonMesh& mesh, typename boost::graph_traits::vertex_descriptor v_keep, diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h index cced686125d..b5579b5204b 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h @@ -1284,14 +1284,21 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh) /// /// \cgalNamedParamsBegin /// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. -/// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` should be available in `PolygonMesh` -/// \cgalParamEnd -/// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `PolygonMesh` /// \cgalParamEnd +/// \cgalParamBegin{vertex_is_constrained_map} a writable property map with `vertex_descriptor` +/// as key and `bool` as `value_type`. `put(pmap, v, true)` will be called for each duplicated +/// vertices and the input one. +/// \cgalParamEnd +/// \cgalParamBegin{output_iterator} an output iterator where `std::vector` can be put. +/// The first vertex of the vector is an input vertex that was non-manifold, +/// the other vertices in the vertex are the new vertices created to fix +/// the non-manifoldness. +/// \cgalParamEnd /// \cgalNamedParamsEnd /// -/// \return true if the triangle face is degenerate +/// \return the number of vertices created template std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, const NamedParameters& np) @@ -1301,14 +1308,33 @@ std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, using boost::get_param; using boost::choose_param; - typedef typename GetVertexPointMap::type VertexPointMap; - VertexPointMap vpm = choose_param(get_param(np, internal_np::vertex_point), - get_property_map(vertex_point, tm)); - typedef boost::graph_traits GT; typedef typename GT::vertex_descriptor vertex_descriptor; typedef typename GT::halfedge_descriptor halfedge_descriptor; + typedef typename GetVertexPointMap::type VertexPointMap; + VertexPointMap vpm = choose_param(get_param(np, internal_np::vertex_point), + get_property_map(vertex_point, tm)); + + typedef typename boost::lookup_named_param_def < + internal_np::vertex_is_constrained_t, + NamedParameters, + internal::No_constraint_pmap//default + > ::type VerticesMap; + VerticesMap cmap + = choose_param(get_param(np, internal_np::vertex_is_constrained), + internal::No_constraint_pmap()); + + typedef typename boost::lookup_named_param_def < + internal_np::output_iterator_t, + NamedParameters, + Emptyset_iterator + > ::type Output_iterator; + Output_iterator out + = choose_param(get_param(np, internal_np::output_iterator), + Emptyset_iterator()); + + internal::Vertex_collector dmap; boost::unordered_set vertices_handled; boost::unordered_set halfedges_handled; @@ -1322,6 +1348,7 @@ std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, vertex_descriptor vd = target(h, tm); if ( !vertices_handled.insert(vd).second ) { + put(cmap, vd, true); // store the originals non_manifold_cones.push_back(h); } else @@ -1341,6 +1368,8 @@ std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, halfedge_descriptor start = h; vertex_descriptor new_vd = add_vertex(tm); ++nb_new_vertices; + put(cmap, new_vd, true); // store the duplicates + dmap.collect_vertices(target(h, tm), new_vd); put(vpm, new_vd, get(vpm, target(h, tm))); set_halfedge(new_vd, h, tm); do{ @@ -1348,8 +1377,8 @@ std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, h=opposite(next(h, tm), tm); } while(h!=start); } + dmap.dump(out); } - return nb_new_vertices; } diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp index 679f96762d3..3bf2ffcd494 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp @@ -43,7 +43,7 @@ void check_triangle_face_degeneracy(const char* fname) CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[3], mesh)); } -// temp left here: tests repair.h +// tests repair.h void test_vertices_merge_and_duplication(const char* fname) { std::ifstream input(fname); @@ -62,10 +62,13 @@ void test_vertices_merge_and_duplication(const char* fname) const std::size_t vertices_after_merge = vertices(mesh).size(); CGAL_assertion(vertices_after_merge == initial_vertices - 1); - CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices(mesh); - const std::size_t final_vertices = vertices(mesh).size(); - CGAL_assertion(final_vertices == vertices_after_merge + 1); - CGAL_assertion(final_vertices == initial_vertices); + std::vector< std::vector > duplicated_vertices; + CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices(mesh, + CGAL::parameters::output_iterator(std::back_inserter(duplicated_vertices))); + const std::size_t final_vertices_size = vertices(mesh).size(); + CGAL_assertion(final_vertices_size == vertices_after_merge + 1); + CGAL_assertion(final_vertices_size == initial_vertices); + CGAL_assertion(duplicated_vertices.size() == 2); } void test_vertex_non_manifoldness(const char* fname) From 99db9a0aaf22e5b6774ed7e1c9d617a013cea5b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 20 Apr 2018 11:50:54 +0200 Subject: [PATCH 18/36] WIP correctly linking halfedges around merged vertices ... also disable the merge between cycles as it is not straight forward it will be always possible --- .../merge_border_vertices.h | 154 +++++++++++++++--- .../test_merging_border_vertices.cpp | 7 +- 2 files changed, 132 insertions(+), 29 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h index 3435e5fd7bd..0dc8560cad5 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h @@ -37,6 +37,7 @@ namespace Polygon_mesh_processing{ namespace internal { +#if 0 // warning: vertices will be altered (sorted) template void detect_identical_vertices(std::vector& vertices, @@ -76,11 +77,77 @@ void detect_identical_vertices(std::vector& vertices, ++i; } } +#endif + +template +struct Less_on_point_of_target +{ + typedef typename boost::graph_traits::halfedge_descriptor + halfedge_descriptor; + typedef typename boost::property_traits::reference Point; + + Less_on_point_of_target(const PM& pm, + const VertexPointMap& vpm) + : pm(pm), + vpm(vpm) + {} + + bool operator()(halfedge_descriptor h1, + halfedge_descriptor h2) const + { + return get(vpm, target(h1, pm)) < get(vpm, target(h2, pm)); + } + + const PM& pm; + const VertexPointMap& vpm; +}; + + +// warning: cycle_hedges will be altered (sorted) +template +void detect_identical_vertices(std::vector& cycle_hedges, + std::vector< std::vector >& hedges_with_identical_point_target, + const PolygonMesh& pm, + Vpm vpm) +{ + // sort vertices using their point to ease the detection + // of vertices with identical points + Less_on_point_of_target less(pm, vpm); + std::sort( cycle_hedges.begin(), cycle_hedges.end(), less); + + std::size_t nbv=cycle_hedges.size(); + std::size_t i=1; + + while(i!=nbv) + { + if ( get(vpm, target(cycle_hedges[i], pm)) == + get(vpm, target(cycle_hedges[i-1], pm)) ) + { + hedges_with_identical_point_target.push_back( std::vector() ); + hedges_with_identical_point_target.back().push_back(cycle_hedges[i-1]); + hedges_with_identical_point_target.back().push_back(cycle_hedges[i]); + while(++i!=nbv) + { + if ( get(vpm, target(cycle_hedges[i], pm)) == + get(vpm, target(cycle_hedges[i-1], pm)) ) + hedges_with_identical_point_target.back().push_back(cycle_hedges[i]); + else + { + ++i; + break; + } + } + } + else + ++i; + } +} } // end of internal /// \todo document me /// It should probably go into BGL package +/// It should make sense to also return the length of each cycle template OutputIterator extract_boundary_cycles(PolygonMesh& pm, @@ -103,33 +170,44 @@ extract_boundary_cycles(PolygonMesh& pm, /// \ingroup PMP_repairing_grp /// \todo document me -template -void merge_boundary_vertices(const VertexRange& vertices, - PolygonMesh& pm) +/// we merge the all the target of the halfedges in `hedges` +/// hedges must be sorted along the cycle +template +void merge_boundary_vertices_in_cycle(const HalfedgeRange& sorted_hedges, + PolygonMesh& pm) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - vertex_descriptor v_kept=*boost::begin(vertices); + halfedge_descriptor in_h_kept = *boost::begin(sorted_hedges); + halfedge_descriptor out_h_kept = next(in_h_kept, pm); + vertex_descriptor v_kept=target(in_h_kept, pm); + std::vector vertices_to_rm; - BOOST_FOREACH(vertex_descriptor vd, vertices) + BOOST_FOREACH(halfedge_descriptor in_h_rm, sorted_hedges) { - if (vd==v_kept) continue; // skip identical vertices + vertex_descriptor vd = target(in_h_rm, pm); + if (vd==v_kept) continue; // skip identical vertices (in particular this skips the first halfedge) if (edge(vd, v_kept, pm).second) continue; // skip null edges bool shall_continue=false; - BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(v_kept, pm)) + BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(v_kept, pm)) { - if (edge(vd, source(hd, pm), pm).second) + if (edge(vd, source(h, pm), pm).second) { shall_continue=true; break; } } if (shall_continue) continue; // skip vertices already incident to the same vertex - - internal::update_target_vertex(halfedge(vd, pm), v_kept, pm); + // update the vertex of the halfedges incident to the vertex to remove + internal::update_target_vertex(in_h_rm, v_kept, pm); + // update next/prev pointers around the 2 vertices to be merged + halfedge_descriptor out_h_rm = next(in_h_rm, pm); + set_next(in_h_kept, out_h_rm, pm); + set_next(in_h_rm, out_h_kept, pm); vertices_to_rm.push_back(vd); + out_h_kept=out_h_rm; } BOOST_FOREACH(vertex_descriptor vd, vertices_to_rm) @@ -139,30 +217,54 @@ void merge_boundary_vertices(const VertexRange& vertices, /// \ingroup PMP_repairing_grp /// \todo document me template -void merge_duplicated_vertices_in_boundary_cycle(typename boost::graph_traits::halfedge_descriptor h, - PolygonMesh& pm, - const NamedParameter& np) +void merge_duplicated_vertices_in_boundary_cycle( + typename boost::graph_traits::halfedge_descriptor h, + PolygonMesh& pm, + const NamedParameter& np) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef typename GetVertexPointMap::const_type Vpm; Vpm vpm = choose_param(get_param(np, internal_np::vertex_point), get_const_property_map(vertex_point, pm)); - // collect all the vertices of the cycle - std::vector vertices; + // collect all the halfedges of the cycle + std::vector cycle_hedges; halfedge_descriptor start=h; do{ - vertices.push_back(target(h,pm)); + cycle_hedges.push_back(h); h=next(h, pm); }while(start!=h); - std::vector< std::vector > identical_vertices; - internal::detect_identical_vertices(vertices, identical_vertices, vpm); + std::vector< std::vector > hedges_with_identical_point_target; + internal::detect_identical_vertices(cycle_hedges, hedges_with_identical_point_target, pm, vpm); - BOOST_FOREACH(const std::vector& vrtcs, identical_vertices) - merge_boundary_vertices(vrtcs, pm); + BOOST_FOREACH(const std::vector& hedges, + hedges_with_identical_point_target) + { + start=hedges.front(); + // collect all halfedges in the cycle + std::vector sorted_hedges; + h=start; + do{ + sorted_hedges.push_back(h); + do + { + h=next(h, pm); + } + while( get(vpm, target(h, pm)) != get(vpm, target(start, pm)) ); + } + while(h!=start); + + if (sorted_hedges.size() != hedges.size()) + { + std::cerr << "WARNING: cycle broken at " << get(vpm, target(start, pm)) << ". Skipped\n"; + std::cout << sorted_hedges.size() << " vs " << hedges.size() << "\n"; + CGAL_assertion(sorted_hedges.size() == hedges.size()); + continue; + } + merge_boundary_vertices_in_cycle(sorted_hedges, pm); + } } /// \ingroup PMP_repairing_grp @@ -180,7 +282,7 @@ void merge_duplicated_vertices_in_boundary_cycles( PolygonMesh& pm, merge_duplicated_vertices_in_boundary_cycle(h, pm, np); } - +#if 0 /// \ingroup PMP_repairing_grp /// \todo document me template @@ -207,8 +309,7 @@ void merge_duplicated_boundary_vertices( PolygonMesh& pm, BOOST_FOREACH(const std::vector& vrtcs, identical_vertices) merge_boundary_vertices(vrtcs, pm); } - - +#endif template void merge_duplicated_vertices_in_boundary_cycles(PolygonMesh& pm) @@ -221,15 +322,16 @@ void merge_duplicated_vertices_in_boundary_cycle( typename boost::graph_traits::halfedge_descriptor h, PolygonMesh& pm) { - merge_duplicated_vertices_in_boundary_cycles(h, pm, parameters::all_default()); + merge_duplicated_vertices_in_boundary_cycle(h, pm, parameters::all_default()); } +#if 0 template void merge_duplicated_boundary_vertices(PolygonMesh& pm) { merge_duplicated_boundary_vertices(pm, parameters::all_default()); } - +#endif } } // end of CGAL::Polygon_mesh_processing diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp index 1abc453207a..a3052bbd2fd 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp @@ -38,6 +38,7 @@ void test_merge_duplicated_vertices_in_boundary_cycles(const char* fname, } } +#if 0 void test_merge_duplicated_boundary_vertices(const char* fname, std::size_t expected_nb_vertices) { @@ -64,21 +65,21 @@ void test_merge_duplicated_boundary_vertices(const char* fname, output << mesh; } } - +#endif int main(int argc, char** argv) { if (argc==1) { test_merge_duplicated_vertices_in_boundary_cycles("data/merge_points.off", 43); - test_merge_duplicated_boundary_vertices("data/merge_points.off", 40); + // test_merge_duplicated_boundary_vertices("data/merge_points.off", 40); } else { for (int i=1; i< argc; ++i) { test_merge_duplicated_vertices_in_boundary_cycles(argv[i], 0); - test_merge_duplicated_boundary_vertices(argv[i], 0); + // test_merge_duplicated_boundary_vertices(argv[i], 0); } } return 0; From ee3636d57eb86c59b3ad1c5267576dbeed5f86e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 20 Apr 2018 14:01:24 +0200 Subject: [PATCH 19/36] directly sort halfedges and use the ordering to detect illegal merges a merge is considered as illegal if it makes to vertices to be merged unreachable. For now if a cycle contain an illegal merge, all merges of the cycle are ignored. --- .../merge_border_vertices.h | 93 +++++++++++-------- 1 file changed, 55 insertions(+), 38 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h index 0dc8560cad5..4a3ce2f8406 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h @@ -92,10 +92,14 @@ struct Less_on_point_of_target vpm(vpm) {} - bool operator()(halfedge_descriptor h1, - halfedge_descriptor h2) const + bool operator()(const std::pair& h1, + const std::pair& h2) const { - return get(vpm, target(h1, pm)) < get(vpm, target(h2, pm)); + if ( get(vpm, target(h1.first, pm)) < get(vpm, target(h2.first, pm)) ) + return true; + if ( get(vpm, target(h1.first, pm)) > get(vpm, target(h2.first, pm)) ) + return false; + return h1.second < h2.second; } const PM& pm; @@ -105,10 +109,11 @@ struct Less_on_point_of_target // warning: cycle_hedges will be altered (sorted) template -void detect_identical_vertices(std::vector& cycle_hedges, - std::vector< std::vector >& hedges_with_identical_point_target, - const PolygonMesh& pm, - Vpm vpm) +void detect_identical_mergeable_vertices( + std::vector< std::pair >& cycle_hedges, + std::vector< std::vector >& hedges_with_identical_point_target, + const PolygonMesh& pm, + Vpm vpm) { // sort vertices using their point to ease the detection // of vertices with identical points @@ -118,19 +123,27 @@ void detect_identical_vertices(std::vector& cycle_hedges, std::size_t nbv=cycle_hedges.size(); std::size_t i=1; + std::set< std::pair > intervals; + while(i!=nbv) { - if ( get(vpm, target(cycle_hedges[i], pm)) == - get(vpm, target(cycle_hedges[i-1], pm)) ) + if ( get(vpm, target(cycle_hedges[i].first, pm)) == + get(vpm, target(cycle_hedges[i-1].first, pm)) ) { hedges_with_identical_point_target.push_back( std::vector() ); - hedges_with_identical_point_target.back().push_back(cycle_hedges[i-1]); - hedges_with_identical_point_target.back().push_back(cycle_hedges[i]); + hedges_with_identical_point_target.back().push_back(cycle_hedges[i-1].first); + hedges_with_identical_point_target.back().push_back(cycle_hedges[i].first); + intervals.insert( std::make_pair(cycle_hedges[i-1].second, cycle_hedges[i].second) ); + std::size_t previous = cycle_hedges[i].second; while(++i!=nbv) { - if ( get(vpm, target(cycle_hedges[i], pm)) == - get(vpm, target(cycle_hedges[i-1], pm)) ) - hedges_with_identical_point_target.back().push_back(cycle_hedges[i]); + if ( get(vpm, target(cycle_hedges[i].first, pm)) == + get(vpm, target(cycle_hedges[i-1].first, pm)) ) + { + hedges_with_identical_point_target.back().push_back(cycle_hedges[i].first); + intervals.insert( std::make_pair(previous, cycle_hedges[i].second) ); + previous = cycle_hedges[i].second; + } else { ++i; @@ -141,6 +154,27 @@ void detect_identical_vertices(std::vector& cycle_hedges, else ++i; } + + // check that intervals are disjoint or strictly nested + // if there is only one issue we drop the whole cycle. + /// \todo shall we try to be more conservative? + if (hedges_with_identical_point_target.empty()) return; + std::set< std::pair >::iterator it1 = intervals.begin(), + end2 = intervals.end(), + end1 = cpp11::prev(end2), + it2; + for (; it1!=end1; ++it1) + for(it2=cpp11::next(it1); it2!= end2; ++it2 ) + { + CGAL_assertion(it1->firstfirst); + CGAL_assertion(it1->first < it1->second && it2->first < it2->second); + if (it1->second > it2->first && it2->second > it1->second) + { + std::cerr << "Merging is skipt to avoid bad cycle connections\n"; + hedges_with_identical_point_target.clear(); + return; + } + } } } // end of internal @@ -229,41 +263,24 @@ void merge_duplicated_vertices_in_boundary_cycle( get_const_property_map(vertex_point, pm)); // collect all the halfedges of the cycle - std::vector cycle_hedges; + std::vector< std::pair > cycle_hedges; halfedge_descriptor start=h; + std::size_t index=0; do{ - cycle_hedges.push_back(h); + cycle_hedges.push_back( std::make_pair(h, index) ); h=next(h, pm); + ++index; }while(start!=h); std::vector< std::vector > hedges_with_identical_point_target; - internal::detect_identical_vertices(cycle_hedges, hedges_with_identical_point_target, pm, vpm); + internal::detect_identical_mergeable_vertices(cycle_hedges, hedges_with_identical_point_target, pm, vpm); BOOST_FOREACH(const std::vector& hedges, hedges_with_identical_point_target) { start=hedges.front(); - // collect all halfedges in the cycle - std::vector sorted_hedges; - h=start; - do{ - sorted_hedges.push_back(h); - do - { - h=next(h, pm); - } - while( get(vpm, target(h, pm)) != get(vpm, target(start, pm)) ); - } - while(h!=start); - - if (sorted_hedges.size() != hedges.size()) - { - std::cerr << "WARNING: cycle broken at " << get(vpm, target(start, pm)) << ". Skipped\n"; - std::cout << sorted_hedges.size() << " vs " << hedges.size() << "\n"; - CGAL_assertion(sorted_hedges.size() == hedges.size()); - continue; - } - merge_boundary_vertices_in_cycle(sorted_hedges, pm); + // hedges are sorted along the cycle + merge_boundary_vertices_in_cycle(hedges, pm); } } From e299309a22a9598a6052fec3a75542113cdf2a63 Mon Sep 17 00:00:00 2001 From: Konstantinos Katrioplas Date: Tue, 22 May 2018 13:14:12 +0200 Subject: [PATCH 20/36] add missing named parameter documentation --- .../doc/Polygon_mesh_processing/NamedParameters.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt index 839bd7b52ff..002f2a837bb 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt @@ -365,6 +365,17 @@ should be considered as part of the clipping volume or not. \cgalNPEnd +\cgalNPBegin{output_iterator} \anchor PMP_output_iterator +Iterator where `std::vector` can be put. +The first vertex of the vector is an input vertex that was non-manifold, +the other vertices in the vertex are the new vertices created to fix +the non-manifoldness. +\n +\b Type : `iterator` \n +\b Default `Emptyset_iterator` +\cgalNPEnd + + \cgalNPTableEnd */ From b51fa000a47e41e078f259d07795393c33141e31 Mon Sep 17 00:00:00 2001 From: Konstantinos Katrioplas Date: Wed, 23 May 2018 12:17:46 +0200 Subject: [PATCH 21/36] documentation on merge border vertices functions --- .../merge_border_vertices.h | 62 ++++++++++++++++--- 1 file changed, 54 insertions(+), 8 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h index 4a3ce2f8406..2406c6d72de 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h @@ -179,9 +179,18 @@ void detect_identical_mergeable_vertices( } // end of internal -/// \todo document me -/// It should probably go into BGL package -/// It should make sense to also return the length of each cycle +/// \ingroup PMP_repairing_grp +/// extracts boundary cycles as a list of halfedges. +/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph`. +/// @tparam OutputIterator a model of `OutputIterator` holding objects of type +/// `boost::graph_traits::%halfedge_descriptor` +/// +/// @param pm the polygon mesh. +/// @param out an output iterator where the list of halfedges will be put. +/// +/// @todo Maybe move to BGL +/// @todo It should make sense to also return the length of each cycle. +/// @todo It should probably go into BGL package. template OutputIterator extract_boundary_cycles(PolygonMesh& pm, @@ -203,9 +212,16 @@ extract_boundary_cycles(PolygonMesh& pm, } /// \ingroup PMP_repairing_grp -/// \todo document me -/// we merge the all the target of the halfedges in `hedges` -/// hedges must be sorted along the cycle +/// merges target vertices of a list of halfedges. +/// Halfedges must be sorted in the list. +/// +/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph`. +/// @tparam HalfedgeRange a range of halfedge descriptors of `PolygonMesh`, model of `Range`. +/// +/// @param sorted_hedges a sorted list of halfedges. +/// @param pm the polygon mesh which contains the list of halfedges. +/// +/// @todo rename me to `merge_vertices_in_range` because I merge any king of vertices in the list. template void merge_boundary_vertices_in_cycle(const HalfedgeRange& sorted_hedges, PolygonMesh& pm) @@ -249,7 +265,22 @@ void merge_boundary_vertices_in_cycle(const HalfedgeRange& sorted_hedges, } /// \ingroup PMP_repairing_grp -/// \todo document me +/// merges identical vertices around a cycle of connected edges. +/// +/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph`. +/// @tparam NamedParameter a sequence of \ref pmp_namedparameters "Named Parameters". +/// +/// @param h a halfedge that belongs to the cycle. +/// @param pm the polygon mesh which containts the cycle. +/// @param np optional parameter of \ref pmp_namedparameters "Named Parameters" listed below. +/// +/// \cgalNamedParamsBegin +/// \cgalParamBegin{vertex_point_map} +/// the property map with the points associated to the vertices of `pm`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `PolygonMesh` +/// \cgalParamEnd +/// \cgalNamedParamsEnd template void merge_duplicated_vertices_in_boundary_cycle( typename boost::graph_traits::halfedge_descriptor h, @@ -285,7 +316,22 @@ void merge_duplicated_vertices_in_boundary_cycle( } /// \ingroup PMP_repairing_grp -/// \todo document me +/// extracts boundary cycles and merges the duplicated +/// vertices of each cycle. +/// +/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph`. +/// @tparam NamedParameter a sequence of \ref pmp_namedparameters "Named Parameters". +/// +/// @param pm the polygon mesh which containts the cycle. +/// @param np optional parameter of \ref pmp_namedparameters "Named Parameters" listed below. +/// +/// \cgalNamedParamsBegin +/// \cgalParamBegin{vertex_point_map} +/// the property map with the points associated to the vertices of `pm`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `PolygonMesh` +/// \cgalParamEnd +/// \cgalNamedParamsEnd template void merge_duplicated_vertices_in_boundary_cycles( PolygonMesh& pm, const NamedParameter& np) From aed0cb1834060135476ffc921525e237502e7a89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 3 Jul 2018 15:47:35 +0200 Subject: [PATCH 22/36] remove extra comma --- .../internal/Isotropic_remeshing/remesh_impl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index 9686f4c271b..8826e7be23c 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -365,7 +365,7 @@ namespace internal { BOOST_FOREACH(face_descriptor f, face_range) { - if (is_degenerate_triangle_face(f, mesh_,)){ + if (is_degenerate_triangle_face(f, mesh_)){ continue; } Patch_id pid = get_patch_id(f); From 0c9fea5d28f0dd5b543d1d073178fca81ec1a134 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Thu, 19 Jul 2018 17:03:30 +0200 Subject: [PATCH 23/36] Fixed new named parameter test --- BGL/test/BGL/test_cgal_bgl_named_params.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BGL/test/BGL/test_cgal_bgl_named_params.cpp b/BGL/test/BGL/test_cgal_bgl_named_params.cpp index c0081450d1e..6bd99f2bdc0 100644 --- a/BGL/test/BGL/test_cgal_bgl_named_params.cpp +++ b/BGL/test/BGL/test_cgal_bgl_named_params.cpp @@ -163,7 +163,7 @@ void test(const NamedParameters& np) check_same_type<41>(get_param(np, CGAL::internal_np::verbosity_level)); check_same_type<42>(get_param(np, CGAL::internal_np::projection_functor)); check_same_type<46>(get_param(np, CGAL::internal_np::apply_per_connected_component)); - check_same_type472>(get_param(np, CGAL::internal_np::output_iterator)); + check_same_type<47>(get_param(np, CGAL::internal_np::output_iterator)); } int main() From 3b9464f54974b53bb230ee9c63861dffd06214e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Thu, 19 Jul 2018 17:05:07 +0200 Subject: [PATCH 24/36] Replaced No_constraint_pmap with Constant_property_map --- .../CGAL/Polygon_mesh_processing/helpers.h | 16 ----------- .../Isotropic_remeshing/remesh_impl.h | 17 +----------- .../random_perturbation.h | 4 +-- .../CGAL/Polygon_mesh_processing/remesh.h | 27 +++++++++---------- .../CGAL/Polygon_mesh_processing/repair.h | 7 ++--- Property_map/include/CGAL/property_map.h | 3 ++- 6 files changed, 22 insertions(+), 52 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h index ea38a91b835..b41c37e1c80 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h @@ -25,28 +25,12 @@ #include #include - namespace CGAL { namespace Polygon_mesh_processing { namespace internal { -template -struct No_constraint_pmap -{ -public: - typedef Descriptor key_type; - typedef bool value_type; - typedef value_type& reference; - typedef boost::read_write_property_map_tag category; - - friend bool get(const No_constraint_pmap& , const key_type& ) { - return false; - } - friend void put(No_constraint_pmap& , const key_type& , const bool ) {} -}; - template struct Vertex_collector { diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index 8826e7be23c..428d49a6309 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -91,21 +91,6 @@ namespace internal { }; // A property map - template - struct No_constraint_pmap - { - public: - typedef Descriptor key_type; - typedef bool value_type; - typedef value_type& reference; - typedef boost::read_write_property_map_tag category; - - friend bool get(const No_constraint_pmap& , const key_type& ) { - return false; - } - friend void put(No_constraint_pmap& , const key_type& , const bool ) {} - }; - template struct Border_constraint_pmap { @@ -1514,7 +1499,7 @@ private: // update status using constrained edge map if (!boost::is_same >::value) + Constant_property_map >::value) { BOOST_FOREACH(edge_descriptor e, edges(mesh_)) { diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/random_perturbation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/random_perturbation.h index ea7362e1a95..8a210843b1e 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/random_perturbation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/random_perturbation.h @@ -174,10 +174,10 @@ void random_perturbation(VertexRange vertices typedef typename boost::lookup_named_param_def < internal_np::vertex_is_constrained_t, NamedParameters, - internal::No_constraint_pmap//default + Constant_property_map // default > ::type VCMap; VCMap vcmap = choose_param(get_param(np, internal_np::vertex_is_constrained), - internal::No_constraint_pmap()); + Constant_property_map(false)); unsigned int seed = choose_param(get_param(np, internal_np::random_seed), -1); bool do_project = choose_param(get_param(np, internal_np::do_project), true); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h index d87cd651b6a..40994d53a87 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -175,18 +175,18 @@ void isotropic_remeshing(const FaceRange& faces typedef typename boost::lookup_named_param_def < internal_np::edge_is_constrained_t, NamedParameters, - internal::No_constraint_pmap//default + Constant_property_map // default (no constraint pmap) > ::type ECMap; - ECMap ecmap = choose_param(get_param(np, internal_np::edge_is_constrained) - , internal::No_constraint_pmap()); + ECMap ecmap = choose_param(get_param(np, internal_np::edge_is_constrained), + Constant_property_map(false)); typedef typename boost::lookup_named_param_def < internal_np::vertex_is_constrained_t, NamedParameters, - internal::No_constraint_pmap//default + Constant_property_map // default (no constraint pmap) > ::type VCMap; VCMap vcmap = choose_param(get_param(np, internal_np::vertex_is_constrained), - internal::No_constraint_pmap()); + Constant_property_map(false)); bool protect = choose_param(get_param(np, internal_np::protect_constraints), false); typedef typename boost::lookup_named_param_def < @@ -351,22 +351,21 @@ void split_long_edges(const EdgeRange& edges typedef typename boost::lookup_named_param_def < internal_np::edge_is_constrained_t, NamedParameters, - internal::No_constraint_pmap//default + Constant_property_map // default (no constraint pmap) > ::type ECMap; ECMap ecmap = choose_param(get_param(np, internal_np::edge_is_constrained), - internal::No_constraint_pmap()); + Constant_property_map(false)); typename internal::Incremental_remesher, + Constant_property_map, // no constraint pmap internal::Connected_components_pmap, FIMap > - remesher(pmesh, vpmap, false/*protect constraints*/ - , ecmap - , internal::No_constraint_pmap() - , internal::Connected_components_pmap(faces(pmesh), pmesh, ecmap, fimap, false) - , fimap - , false/*need aabb_tree*/); + remesher(pmesh, vpmap, false/*protect constraints*/, ecmap, + Constant_property_map(false), + internal::Connected_components_pmap(faces(pmesh), pmesh, ecmap, fimap, false), + fimap, + false/*need aabb_tree*/); remesher.split_long_edges(edges, max_length); } diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h index b5579b5204b..e03c785e990 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h @@ -24,12 +24,13 @@ #include - #include #include + #include #include #include +#include #include #include @@ -1319,11 +1320,11 @@ std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, typedef typename boost::lookup_named_param_def < internal_np::vertex_is_constrained_t, NamedParameters, - internal::No_constraint_pmap//default + Constant_property_map // default (no constraint pmap) > ::type VerticesMap; VerticesMap cmap = choose_param(get_param(np, internal_np::vertex_is_constrained), - internal::No_constraint_pmap()); + Constant_property_map(false)); typedef typename boost::lookup_named_param_def < internal_np::output_iterator_t, diff --git a/Property_map/include/CGAL/property_map.h b/Property_map/include/CGAL/property_map.h index f589496dcba..737c82320c3 100644 --- a/Property_map/include/CGAL/property_map.h +++ b/Property_map/include/CGAL/property_map.h @@ -462,10 +462,11 @@ make_property_map(const std::vector& v) template struct Constant_property_map { - const ValueType default_value; + ValueType default_value; typedef KeyType key_type; typedef ValueType value_type; + typedef value_type& reference; typedef boost::read_write_property_map_tag category; Constant_property_map(const value_type& default_value = value_type()) : default_value (default_value) { } From fc41d58bfdc150d9cffeca0e0f804445d0eac0ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Fri, 20 Jul 2018 12:16:13 +0200 Subject: [PATCH 25/36] Added some missing \sa for Vector_23 --- Kernel_23/doc/Kernel_23/Concepts/GeomObjects.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Kernel_23/doc/Kernel_23/Concepts/GeomObjects.h b/Kernel_23/doc/Kernel_23/Concepts/GeomObjects.h index b120d75b38c..034890bb56c 100644 --- a/Kernel_23/doc/Kernel_23/Concepts/GeomObjects.h +++ b/Kernel_23/doc/Kernel_23/Concepts/GeomObjects.h @@ -721,6 +721,8 @@ public: \cgalHasModel `CGAL::Vector_2` \sa `Kernel::ComputeDeterminant_2` + \sa `Kernel::ComputeScalarProduct_2` + \sa `Kernel::ComputeSquaredLength_2` \sa `Kernel::ComputeX_2` \sa `Kernel::ComputeY_2` \sa `Kernel::ComputeHx_2` @@ -753,7 +755,10 @@ A type representing vectors in three dimensions. \cgalHasModel `CGAL::Vector_3` -\sa `Kernel::ComputeDeterminant_3` +\sa `Kernel::CompareDihedralAngle_3` +\sa `Kernel::ComputeDeterminant_3` +\sa `Kernel::ComputeScalarProduct_3` +\sa `Kernel::ComputeSquaredLength_3` \sa `Kernel::ComputeX_3` \sa `Kernel::ComputeY_3` \sa `Kernel::ComputeZ_3` From 49a971e9c2ae57864bbafccfc0b65c3a535c8a2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Fri, 20 Jul 2018 17:30:40 +0200 Subject: [PATCH 26/36] Various improvements/fixes to degenerate/needle/cap functions --- .../NamedParameters.txt | 13 +- .../CGAL/Polygon_mesh_processing/helpers.h | 352 +++++++++--------- .../Isotropic_remeshing/remesh_impl.h | 88 ++--- .../CGAL/Polygon_mesh_processing/remesh.h | 7 +- .../CGAL/Polygon_mesh_processing/repair.h | 122 ++++-- .../data_degeneracies/caps_and_needles.off | 17 + .../test_predicates.cpp | 244 ++++++++---- .../Plugins/PMP/Selection_plugin.cpp | 4 +- .../demo/Polyhedron/Scene_polyhedron_item.cpp | 2 +- .../Scene_polyhedron_selection_item.cpp | 3 +- .../Polyhedron/Scene_surface_mesh_item.cpp | 2 +- .../include/CGAL/statistics_helpers.h | 25 +- 12 files changed, 519 insertions(+), 360 deletions(-) create mode 100644 Polygon_mesh_processing/test/Polygon_mesh_processing/data_degeneracies/caps_and_needles.off diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt index 002f2a837bb..a2c5456db34 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt @@ -337,7 +337,7 @@ of a mesh independently.\n Parameter used to pass a visitor class to a function. Its type and behavior depend on the visited function. \n \b Type : `A class` \n -\b Default Specific to the function visited +\b Default : Specific to the function visited \cgalNPEnd \cgalNPBegin{throw_on_self_intersection} \anchor PMP_throw_on_self_intersection @@ -364,18 +364,13 @@ should be considered as part of the clipping volume or not. \b Default value is `true` \cgalNPEnd - \cgalNPBegin{output_iterator} \anchor PMP_output_iterator -Iterator where `std::vector` can be put. -The first vertex of the vector is an input vertex that was non-manifold, -the other vertices in the vertex are the new vertices created to fix -the non-manifoldness. +Parameter to pass an output iterator. \n -\b Type : `iterator` \n -\b Default `Emptyset_iterator` +\b Type : a model of `OutputIterator` \n +\b Default : `Emptyset_iterator` \cgalNPEnd - \cgalNPTableEnd */ diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h index b41c37e1c80..8e9cfeef182 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h @@ -1,4 +1,4 @@ -// Copyright (c) 2015 GeometryFactory (France). +// Copyright (c) 2015, 2018 GeometryFactory (France). // All rights reserved. // // This file is part of CGAL (www.cgal.org). @@ -17,7 +17,8 @@ // SPDX-License-Identifier: GPL-3.0+ // // -// Author(s) : Konstantinos Katrioplas +// Author(s) : Konstantinos Katrioplas, +// Mael Rouxel-Labbé #ifndef CGAL_POLYGON_MESH_PROCESSING_HELPERS_H #define CGAL_POLYGON_MESH_PROCESSING_HELPERS_H @@ -25,86 +26,40 @@ #include #include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + namespace CGAL { namespace Polygon_mesh_processing { -namespace internal { - -template -struct Vertex_collector -{ - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - void collect_vertices(vertex_descriptor v1, vertex_descriptor v2) - { - std::vector& verts = collections[v1]; - if (verts.empty()) - verts.push_back(v1); - verts.push_back(v2); - } - - void dump(OutputIterator out) - { - typedef std::pair > Pair_type; - BOOST_FOREACH(const Pair_type& p, collections) - { - *out++=p.second; - } - } - - std::map > collections; -}; - -template -struct Vertex_collector -{ - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - void collect_vertices(vertex_descriptor, vertex_descriptor) - {} - - void dump(Emptyset_iterator) - {} -}; - -// used only for testing -template -void merge_identical_points(PolygonMesh& mesh, - typename boost::graph_traits::vertex_descriptor v_keep, - typename boost::graph_traits::vertex_descriptor v_rm) -{ - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - halfedge_descriptor h = halfedge(v_rm, mesh); - halfedge_descriptor start = h; - - do{ - set_target(h, v_keep, mesh); - h = opposite(next(h, mesh), mesh); - } while( h != start ); - - remove_vertex(v_rm, mesh); -} -} // end internal - /// \ingroup PMP_repairing_grp -/// checks whether a vertex is non-manifold. +/// checks whether a vertex of a triangle mesh is non-manifold. /// -/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph` +/// @tparam TriangleMesh a model of `HalfedgeListGraph` /// -/// @param v the vertex -/// @param tm triangle mesh containing v +/// @param v a vertex of `tm` +/// @param tm a triangle mesh containing `v` /// -/// \return true if the vertrex is non-manifold -template -bool is_non_manifold_vertex(typename boost::graph_traits::vertex_descriptor v, - const PolygonMesh& tm) +/// \return `true` if the vertrex is non-manifold, `false` otherwise. +template +bool is_non_manifold_vertex(typename boost::graph_traits::vertex_descriptor v, + const TriangleMesh& tm) { CGAL_assertion(CGAL::is_triangle_mesh(tm)); - typedef boost::graph_traits GT; - typedef typename GT::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; boost::unordered_set halfedges_handled; - BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(v, tm)) halfedges_handled.insert(h); @@ -121,28 +76,28 @@ bool is_non_manifold_vertex(typename boost::graph_traits::vertex_de /// \ingroup PMP_repairing_grp /// checks whether an edge is degenerate. -/// An edge is considered degenerate if the points of its vertices are identical. +/// An edge is considered degenerate if the geometric positions of its two extremities are identical. /// /// @tparam PolygonMesh a model of `HalfedgeGraph` /// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" /// -/// @param e the edge -/// @param pm polygon mesh containing e +/// @param e an edge of `pm` +/// @param pm polygon mesh containing `e` /// @param np optional \ref pmp_namedparameters "Named Parameters" described below /// /// \cgalNamedParamsBegin -/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. -/// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` should be available in `PolygonMesh` +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pm`. +/// The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `PolygonMesh` /// \cgalParamEnd -/// \cgalParamBegin{geom_traits} a geometric traits class instance. -/// The traits class must provide the nested type `Point_3`, -/// and the nested functor : -/// - `Equal_3` to check whether 2 points are identical +/// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// The traits class must provide the nested type `Point_3`, +/// and the nested functor `Equal_3` to check whether two points are identical. /// \cgalParamEnd /// \cgalNamedParamsEnd /// -/// \return true if the edge is degenerate +/// \return `true` if the edge `e` is degenerate, `false` otherwise. template bool is_degenerate_edge(typename boost::graph_traits::edge_descriptor e, const PolygonMesh& pm, @@ -154,12 +109,11 @@ bool is_degenerate_edge(typename boost::graph_traits::edge_descript typedef typename GetVertexPointMap::const_type VertexPointMap; VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), get_const_property_map(vertex_point, pm)); + typedef typename GetGeomTraits::type Traits; Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits()); - if ( traits.equal_3_object()(get(vpmap, target(e, pm)), get(vpmap, source(e, pm))) ) - return true; - return false; + return traits.equal_3_object()(get(vpmap, source(e, pm)), get(vpmap, target(e, pm))); } template @@ -171,34 +125,34 @@ bool is_degenerate_edge(typename boost::graph_traits::edge_descript /// \ingroup PMP_repairing_grp /// checks whether a triangle face is degenerate. -/// A triangle face is degenerate if its points are collinear. +/// A triangle face is considered degenerate if the geometric positions of its vertices are collinear. /// /// @tparam TriangleMesh a model of `FaceGraph` /// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" /// -/// @param f the triangle face -/// @param tm triangle mesh containing f +/// @param f a triangle face of `tm` +/// @param tm a triangle mesh containing `f` /// @param np optional \ref pmp_namedparameters "Named Parameters" described below /// /// \cgalNamedParamsBegin -/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. -/// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` should be available in `TriangleMesh` +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`. +/// The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `TriangleMesh` /// \cgalParamEnd /// \cgalParamBegin{geom_traits} a geometric traits class instance. -/// The traits class must provide the nested type `Point_3`, -/// and the nested functor : -/// - `Collinear_3` to check whether 3 points are collinear +/// The traits class must provide the nested functor `Collinear_3` +/// to check whether three points are collinear. /// \cgalParamEnd /// \cgalNamedParamsEnd /// -/// \return true if the triangle face is degenerate +/// \return `true` if the face `f` is degenerate, `false` otherwise. template bool is_degenerate_triangle_face(typename boost::graph_traits::face_descriptor f, const TriangleMesh& tm, const NamedParameters& np) { - CGAL_assertion(CGAL::is_triangle_mesh(tm)); + CGAL_precondition(CGAL::is_triangle_mesh(tm)); using boost::get_param; using boost::choose_param; @@ -206,15 +160,15 @@ bool is_degenerate_triangle_face(typename boost::graph_traits::fac typedef typename GetVertexPointMap::const_type VertexPointMap; VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), get_const_property_map(vertex_point, tm)); + typedef typename GetGeomTraits::type Traits; Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits()); - typename boost::graph_traits::halfedge_descriptor hd = halfedge(f,tm); - const typename Traits::Point_3& p1 = get(vpmap, target( hd, tm) ); - const typename Traits::Point_3& p2 = get(vpmap, target(next(hd, tm), tm) ); - const typename Traits::Point_3& p3 = get(vpmap, source( hd, tm) ); - return traits.collinear_3_object()(p1, p2, p3); + typename boost::graph_traits::halfedge_descriptor h = halfedge(f, tm); + return traits.collinear_3_object()(get(vpmap, source(h, tm)), + get(vpmap, target(h, tm)), + get(vpmap, target(next(h, tm), tm))); } template @@ -226,159 +180,185 @@ bool is_degenerate_triangle_face(typename boost::graph_traits::fac /// \ingroup PMP_repairing_grp /// checks whether a triangle face is needle. -/// A triangle is needle if its longest edge is much longer than the shortest one. +/// A triangle is said to be a needle if its longest edge is much longer than its shortest edge. /// /// @tparam TriangleMesh a model of `FaceGraph` /// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" /// -/// @param f the triangle face -/// @param tm triangle mesh containing f -/// @param threshold the cosine of an angle of f. -/// The threshold is in range [0 1] and corresponds to -/// angles between 0 and 90 degrees. +/// @param f a triangle face of `tm` +/// @param tm triangle mesh containing `f` +/// @param threshold a bound on the ratio of the longest edge length and the shortest edge length /// @param np optional \ref pmp_namedparameters "Named Parameters" described below /// /// \cgalNamedParamsBegin -/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. -/// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` should be available in `TriangleMesh` +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`. +/// The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `TriangleMesh` /// \cgalParamEnd /// \cgalParamBegin{geom_traits} a geometric traits class instance. -/// The traits class must provide the nested type `Point_3`. +/// The traits class must provide the nested type `FT` and +/// the nested functor `Compute_squared_distance_3`. /// \cgalParamEnd /// \cgalNamedParamsEnd /// -/// \return true if the triangle face is a needle +/// \return the smallest halfedge if the triangle face is a needle, and a null halfedge otherwise. template -bool is_needle_triangle_face(typename boost::graph_traits::face_descriptor f, - const TriangleMesh& tm, - const double threshold, - const NamedParameters& np) +typename boost::graph_traits::halfedge_descriptor +is_needle_triangle_face(typename boost::graph_traits::face_descriptor f, + const TriangleMesh& tm, + const double threshold, + const NamedParameters& np) { - CGAL_assertion(CGAL::is_triangle_mesh(tm)); - CGAL_assertion(threshold >= 0); - CGAL_assertion(threshold <= 1); + CGAL_precondition(CGAL::is_triangle_mesh(tm)); + CGAL_precondition(threshold >= 1.); using boost::get_param; using boost::choose_param; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename GetVertexPointMap::const_type VertexPointMap; VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), get_const_property_map(vertex_point, tm)); - typedef typename GetGeomTraits::type Traits; - typedef typename Traits::FT FT; - typedef boost::graph_traits GT; - typedef typename GT::halfedge_descriptor halfedge_descriptor; - typedef typename GT::vertex_descriptor vertex_descriptor; - typedef typename boost::property_traits::value_type Point_type; - typedef typename Kernel_traits::Kernel::Vector_3 Vector; - BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(halfedge(f, tm), tm)) + typedef typename GetGeomTraits::type Traits; + Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits()); + + typedef typename Traits::FT FT; + + const halfedge_descriptor h0 = halfedge(f, tm); + FT max_sq_length = - std::numeric_limits::max(), + min_sq_length = std::numeric_limits::max(); + halfedge_descriptor min_h = boost::graph_traits::null_halfedge(); + + BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(h0, tm)) { - vertex_descriptor v0 = source(h, tm); - vertex_descriptor v1 = target(h, tm); - vertex_descriptor v2 = target(next(h, tm), tm); - Vector a = get(vpmap, v0) - get (vpmap, v1); - Vector b = get(vpmap, v2) - get(vpmap, v1); - FT aa = a.squared_length(); - FT bb = b.squared_length(); - FT squared_dot_ab = ((a*b)*(a*b)) / (aa * bb); + const FT sq_length = traits.compute_squared_distance_3_object()(get(vpmap, source(h, tm)), + get(vpmap, target(h, tm))); - if(squared_dot_ab > threshold * threshold) - return true; + if(max_sq_length < sq_length) + max_sq_length = sq_length; + + if(min_sq_length > sq_length) + { + min_h = h; + min_sq_length = sq_length; + } } - return false; + const FT sq_threshold = threshold * threshold; + if(max_sq_length / min_sq_length >= sq_threshold) + { + CGAL_assertion(min_h != boost::graph_traits::null_halfedge()); + return min_h; + } + else + return boost::graph_traits::null_halfedge(); } template -bool is_needle_triangle_face(typename boost::graph_traits::face_descriptor f, - const TriangleMesh& tm, - const double threshold) +typename boost::graph_traits::halfedge_descriptor +is_needle_triangle_face(typename boost::graph_traits::face_descriptor f, + const TriangleMesh& tm, + const double threshold) { return is_needle_triangle_face(f, tm, threshold, parameters::all_default()); } /// \ingroup PMP_repairing_grp /// checks whether a triangle face is a cap. -/// A triangle is a cap if it has an angle very close to 180 degrees. +/// A triangle is said to be a cap if one of the its angles is close to `180` degrees. /// /// @tparam TriangleMesh a model of `FaceGraph` /// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" /// -/// @param f the triangle face -/// @param tm triangle mesh containing f -/// @param threshold the cosine of an angle of f. -/// The threshold is in range [-1 0] and corresponds to -/// angles between 90 and 180 degrees. +/// @param f a triangle face of `tm` +/// @param tm triangle mesh containing `f` +/// @param threshold the cosine of a minimum angle such that if `f` has an angle greater than this bound, +/// it is a cap. The threshold is in range `[-1 0]` and corresponds to an angle +/// between `90` and `180` degrees. /// @param np optional \ref pmp_namedparameters "Named Parameters" described below /// /// \cgalNamedParamsBegin -/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. -/// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` should be available in `TriangleMesh` +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`. +/// The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `TriangleMesh` /// \cgalParamEnd /// \cgalParamBegin{geom_traits} a geometric traits class instance. -/// The traits class must provide the nested type `Point_3` +/// The traits class must provide the nested type `Point_3` /// \cgalParamEnd /// \cgalNamedParamsEnd /// -/// \return true if the triangle face is a cap +/// \return `true` if the triangle face is a cap template -bool is_cap_triangle_face(typename boost::graph_traits::face_descriptor f, - const TriangleMesh& tm, - const double threshold, - const NamedParameters& np) +typename boost::graph_traits::halfedge_descriptor +is_cap_triangle_face(typename boost::graph_traits::face_descriptor f, + const TriangleMesh& tm, + const double threshold, + const NamedParameters& np) { - CGAL_assertion(CGAL::is_triangle_mesh(tm)); - CGAL_assertion(threshold >= -1); - CGAL_assertion(threshold <= 0); + CGAL_precondition(CGAL::is_triangle_mesh(tm)); + CGAL_precondition(threshold >= -1.); + CGAL_precondition(threshold <= 0.); using boost::get_param; using boost::choose_param; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename GetVertexPointMap::const_type VertexPointMap; VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), get_const_property_map(vertex_point, tm)); - typedef typename GetGeomTraits::type Traits; - typedef typename Traits::FT FT; - typedef boost::graph_traits GT; - typedef typename GT::halfedge_descriptor halfedge_descriptor; - typedef typename GT::vertex_descriptor vertex_descriptor; - typedef typename boost::property_traits::value_type Point_type; - typedef typename Kernel_traits::Kernel::Vector_3 Vector; - BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(halfedge(f, tm), tm)) + typedef typename GetGeomTraits::type Traits; + Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits()); + + typedef typename Traits::FT FT; + typedef typename Traits::Vector_3 Vector_3; + + const FT sq_threshold = threshold * threshold; + const halfedge_descriptor h0 = halfedge(f, tm); + + cpp11::array sq_lengths; + int pos = 0; + BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(h0, tm)) { - vertex_descriptor v0 = source(h, tm); - vertex_descriptor v1 = target(h, tm); - vertex_descriptor v2 = target(next(h, tm), tm); - Vector a = get(vpmap, v0) - get (vpmap, v1); - Vector b = get(vpmap, v2) - get(vpmap, v1); - FT aa = a.squared_length(); - FT bb = b.squared_length(); - FT squared_dot_ab = ((a*b)*(a*b)) / (aa * bb); - - if(squared_dot_ab > threshold * threshold) - return true; + sq_lengths[pos++] = traits.compute_squared_distance_3_object()(get(vpmap, source(h, tm)), + get(vpmap, target(h, tm))); } - return false; + + pos = 0; + BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(h0, tm)) + { + const vertex_descriptor v0 = source(h, tm); + const vertex_descriptor v1 = target(h, tm); + const vertex_descriptor v2 = target(next(h, tm), tm); + const Vector_3 a = traits.construct_vector_3_object()(get(vpmap, v1), get(vpmap, v2)); + const Vector_3 b = traits.construct_vector_3_object()(get(vpmap, v1), get(vpmap, v0)); + const FT dot_ab = traits.compute_scalar_product_3_object()(a, b); + const bool neg_sp = (dot_ab <= 0); + const FT sq_a = sq_lengths[(pos+1)%3]; + const FT sq_b = sq_lengths[pos]; + const FT sq_cos = dot_ab * dot_ab / (sq_a * sq_b); + + if(neg_sp && sq_cos >= sq_threshold) + return prev(h, tm); + } + return boost::graph_traits::null_halfedge(); } template -bool is_cap_triangle_face(typename boost::graph_traits::face_descriptor f, - const TriangleMesh& tm, - const double threshold) +typename boost::graph_traits::halfedge_descriptor +is_cap_triangle_face(typename boost::graph_traits::face_descriptor f, + const TriangleMesh& tm, + const double threshold) { return is_cap_triangle_face(f, tm, threshold, parameters::all_default()); } - - - } } // end namespaces CGAL and PMP - - #endif // CGAL_POLYGON_MESH_PROCESSING_HELPERS_H - diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index 428d49a6309..e5e914402db 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -311,6 +311,7 @@ namespace internal { public: Incremental_remesher(PolygonMesh& pmesh , VertexPointMap& vpmap + , const GeomTraits& gt , const bool protect_constraints , EdgeIsConstrainedMap ecmap , VertexIsConstrainedMap vcmap @@ -319,6 +320,7 @@ namespace internal { , const bool build_tree = true)//built by the remesher : mesh_(pmesh) , vpmap_(vpmap) + , gt_(gt) , build_tree_(build_tree) , has_border_(false) , input_triangles_() @@ -350,9 +352,10 @@ namespace internal { BOOST_FOREACH(face_descriptor f, face_range) { - if (is_degenerate_triangle_face(f, mesh_)){ + if(is_degenerate_triangle_face(f, mesh_, parameters::vertex_point_map(vpmap_) + .geom_traits(gt_))) continue; - } + Patch_id pid = get_patch_id(f); input_triangles_.push_back(triangle(f)); input_patch_ids_.push_back(pid); @@ -803,7 +806,8 @@ namespace internal { debug_status_map(); debug_self_intersections(); CGAL_assertion(0 == PMP::remove_degenerate_faces(mesh_, - PMP::parameters::vertex_point_map(vpmap_).geom_traits(GeomTraits()))); + parameters::vertex_point_map(vpmap_) + .geom_traits(gt_))); #endif } @@ -908,7 +912,7 @@ namespace internal { debug_status_map(); CGAL_assertion(0 == PMP::remove_degenerate_faces(mesh_ , PMP::parameters::vertex_point_map(vpmap_) - .geom_traits(GeomTraits()))); + .geom_traits(gt_))); debug_self_intersections(); #endif @@ -950,9 +954,9 @@ namespace internal { else if (is_on_patch(v)) { - Vector_3 vn = PMP::compute_vertex_normal(v, mesh_ - , PMP::parameters::vertex_point_map(vpmap_) - .geom_traits(GeomTraits())); + Vector_3 vn = PMP::compute_vertex_normal(v, mesh_, + parameters::vertex_point_map(vpmap_) + .geom_traits(gt_)); put(propmap_normals, v, vn); Vector_3 move = CGAL::NULL_VECTOR; @@ -1444,20 +1448,8 @@ private: if (f == boost::graph_traits::null_face()) return CGAL::NULL_VECTOR; - halfedge_descriptor hd = halfedge(f, mesh_); - typename boost::property_traits::reference - p = get(vpmap_, target(hd, mesh_)); - hd = next(hd,mesh_); - typename boost::property_traits::reference - q = get(vpmap_, target(hd, mesh_)); - hd = next(hd,mesh_); - typename boost::property_traits::reference - r =get(vpmap_, target(hd, mesh_)); - - if (GeomTraits().collinear_3_object()(p,q,r)) - return CGAL::NULL_VECTOR; - else - return PMP::compute_face_normal(f, mesh_, parameters::vertex_point_map(vpmap_)); + return PMP::compute_face_normal(f, mesh_, parameters::vertex_point_map(vpmap_) + .geom_traits(gt_)); } template @@ -1573,27 +1565,31 @@ private: const bool collapse_constraints) { CGAL_assertion_code(std::size_t nb_done = 0); + boost::unordered_set degenerate_faces; BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(halfedge(v, mesh_), mesh_)) { - if (is_border(h, mesh_)) - continue; - if (is_degenerate_triangle_face(face(h), mesh_)) + if(!is_border(h, mesh_) && + is_degenerate_triangle_face(face(h, mesh_), mesh_, + parameters::vertex_point_map(vpmap_) + .geom_traits(gt_))) degenerate_faces.insert(h); } + while(!degenerate_faces.empty()) { halfedge_descriptor h = *(degenerate_faces.begin()); degenerate_faces.erase(degenerate_faces.begin()); - if (!is_degenerate_triangle_face(face(h), mesh_)) + if (!is_degenerate_triangle_face(face(h, mesh_), mesh_, + parameters::vertex_point_map(vpmap_) + .geom_traits(gt_))) //this can happen when flipping h has consequences further in the mesh continue; //check that opposite is not also degenerate - if (degenerate_faces.find(opposite(h, mesh_)) != degenerate_faces.end()) - degenerate_faces.erase(opposite(h, mesh_)); + degenerate_faces.erase(opposite(h, mesh_)); if(is_border(h, mesh_)) continue; @@ -1639,11 +1635,15 @@ private: short_edges.insert(typename Bimap::value_type(hf, sqlen)); } - if (!is_border(hf, mesh_) - && is_degenerate_triangle_face(face(h), mesh_)) + if(!is_border(hf, mesh_) && + is_degenerate_triangle_face(face(hf, mesh_), mesh_, + parameters::vertex_point_map(vpmap_) + .geom_traits(gt_))) degenerate_faces.insert(hf); - if (!is_border(hfo, mesh_) - && is_degenerate_triangle_face(face(h), mesh_)) + if(!is_border(hfo, mesh_) && + is_degenerate_triangle_face(face(hfo, mesh_), mesh_, + parameters::vertex_point_map(vpmap_) + .geom_traits(gt_))) degenerate_faces.insert(hfo); break; @@ -1660,9 +1660,10 @@ private: BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(he, mesh_)) { - if (is_border(h, mesh_)) - continue; - if (is_degenerate_triangle_face(face(h), mesh_)) + if(!is_border(h, mesh_) && + is_degenerate_triangle_face(face(h, mesh_), mesh_, + parameters::vertex_point_map(vpmap_) + .geom_traits(gt_))) return true; } return false; @@ -1803,10 +1804,10 @@ private: { std::cout << "Test self intersections..."; std::vector > facets; - PMP::self_intersections( - mesh_, - std::back_inserter(facets), - PMP::parameters::vertex_point_map(vpmap_)); + PMP::self_intersections(mesh_, + std::back_inserter(facets), + PMP::parameters::vertex_point_map(vpmap_) + .geom_traits(gt_)); //CGAL_assertion(facets.empty()); std::cout << "done ("<< facets.size() <<" facets)." << std::endl; } @@ -1815,11 +1816,11 @@ private: { std::cout << "Test self intersections..."; std::vector > facets; - PMP::self_intersections( - faces_around_target(halfedge(v, mesh_), mesh_), - mesh_, - std::back_inserter(facets), - PMP::parameters::vertex_point_map(vpmap_)); + PMP::self_intersections(faces_around_target(halfedge(v, mesh_), mesh_), + mesh_, + std::back_inserter(facets), + PMP::parameters::vertex_point_map(vpmap_) + .geom_traits(gt_)); //CGAL_assertion(facets.empty()); std::cout << "done ("<< facets.size() <<" facets)." << std::endl; } @@ -1902,6 +1903,7 @@ private: private: PolygonMesh& mesh_; VertexPointMap& vpmap_; + const GeomTraits& gt_; bool build_tree_; bool has_border_; std::vector trees; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h index 40994d53a87..b50060998f0 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -163,6 +163,7 @@ void isotropic_remeshing(const FaceRange& faces boost::is_default_param(get_param(np, internal_np::projection_functor)); typedef typename GetGeomTraits::type GT; + GT gt = choose_param(get_param(np, internal_np::geom_traits), GT()); typedef typename GetVertexPointMap::type VPMap; VPMap vpmap = choose_param(get_param(np, internal_np::vertex_point), @@ -227,7 +228,7 @@ void isotropic_remeshing(const FaceRange& faces #endif typename internal::Incremental_remesher - remesher(pmesh, vpmap, protect, ecmap, vcmap, fpmap, fimap, need_aabb_tree); + remesher(pmesh, vpmap, gt, protect, ecmap, vcmap, fpmap, fimap, need_aabb_tree); remesher.init_remeshing(faces); #ifdef CGAL_PMP_REMESHING_VERBOSE @@ -340,6 +341,8 @@ void split_long_edges(const EdgeRange& edges using boost::get_param; typedef typename GetGeomTraits::type GT; + GT gt = choose_param(get_param(np, internal_np::geom_traits), GT()); + typedef typename GetVertexPointMap::type VPMap; VPMap vpmap = choose_param(get_param(np, internal_np::vertex_point), get_property_map(vertex_point, pmesh)); @@ -361,7 +364,7 @@ void split_long_edges(const EdgeRange& edges internal::Connected_components_pmap, FIMap > - remesher(pmesh, vpmap, false/*protect constraints*/, ecmap, + remesher(pmesh, vpmap, gt, false/*protect constraints*/, ecmap, Constant_property_map(false), internal::Connected_components_pmap(faces(pmesh), pmesh, ecmap, fimap, false), fimap, diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h index e03c785e990..15c77eeb5c1 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h @@ -159,17 +159,27 @@ struct Less_vertex_point{ } }; +template +OutputIterator +degenerate_faces(const TriangleMesh& tm, + OutputIterator out, + const NamedParameters& np) +{ + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + BOOST_FOREACH(face_descriptor fd, faces(tm)) + { + if(is_degenerate_triangle_face(fd, tm, np)) + *out++ = fd; + } + return out; +} + template OutputIterator degenerate_faces(const TriangleMesh& tm, OutputIterator out) { - typedef typename boost::graph_traits::face_descriptor face_descriptor; - BOOST_FOREACH(face_descriptor fd, faces(tm)) - { - if ( is_degenerate_triangle_face(fd, tm) ) - *out++=fd; - } - return out; + return degenerate_faces(tm, out, CGAL::parameters::all_default()); } // this function remove a border edge even if it does not satisfy the link condition. @@ -717,9 +727,8 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh, // Then, remove triangles made of 3 collinear points std::set degenerate_face_set; - BOOST_FOREACH(face_descriptor fd, faces(tmesh)) - if ( is_degenerate_triangle_face(fd, tmesh, np)) - degenerate_face_set.insert(fd); + degenerate_faces(tmesh, std::inserter(degenerate_face_set, degenerate_face_set.begin()), np); + nb_deg_faces+=degenerate_face_set.size(); // first remove degree 3 vertices that are part of a cap @@ -1274,6 +1283,45 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh) CGAL::Polygon_mesh_processing::parameters::all_default()); } +namespace internal { + +template +struct Vertex_collector +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + + void collect_vertices(vertex_descriptor v1, vertex_descriptor v2) + { + std::vector& verts = collections[v1]; + if(verts.empty()) + verts.push_back(v1); + verts.push_back(v2); + } + + void dump(OutputIterator out) + { + typedef std::pair > Pair_type; + BOOST_FOREACH(const Pair_type& p, collections) { + *out++ = p.second; + } + } + + std::map > collections; +}; + +template +struct Vertex_collector +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + void collect_vertices(vertex_descriptor, vertex_descriptor) + {} + + void dump(Emptyset_iterator) + {} +}; + +} // end namespace internal + /// \ingroup PMP_repairing_grp /// duplicates all non-manifold vertices of the input mesh. /// @@ -1284,22 +1332,22 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh) /// @param np optional \ref pmp_namedparameters "Named Parameters" described below /// /// \cgalNamedParamsBegin -/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. +/// The type of this map is model of `ReadWritePropertyMap`. /// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` should be available in `PolygonMesh` +/// `CGAL::vertex_point_t` should be available in `TriangleMesh` /// \cgalParamEnd /// \cgalParamBegin{vertex_is_constrained_map} a writable property map with `vertex_descriptor` /// as key and `bool` as `value_type`. `put(pmap, v, true)` will be called for each duplicated /// vertices and the input one. /// \cgalParamEnd -/// \cgalParamBegin{output_iterator} an output iterator where `std::vector` can be put. -/// The first vertex of the vector is an input vertex that was non-manifold, -/// the other vertices in the vertex are the new vertices created to fix -/// the non-manifoldness. +/// \cgalParamBegin{output_iterator} a model of `OutputIterator` with value type +/// `std::vector`. The first vertex of the vector is a non-manifold vertex +/// of the input mesh, followed by the new vertices that were created to fix the non-manifoldness. /// \cgalParamEnd /// \cgalNamedParamsEnd /// -/// \return the number of vertices created +/// \return the number of vertices created. template std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, const NamedParameters& np) @@ -1315,7 +1363,7 @@ std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, typedef typename GetVertexPointMap::type VertexPointMap; VertexPointMap vpm = choose_param(get_param(np, internal_np::vertex_point), - get_property_map(vertex_point, tm)); + get_property_map(vertex_point, tm)); typedef typename boost::lookup_named_param_def < internal_np::vertex_is_constrained_t, @@ -1333,37 +1381,46 @@ std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, > ::type Output_iterator; Output_iterator out = choose_param(get_param(np, internal_np::output_iterator), - Emptyset_iterator()); + Emptyset_iterator()); internal::Vertex_collector dmap; boost::unordered_set vertices_handled; boost::unordered_set halfedges_handled; - std::size_t nb_new_vertices=0; + std::size_t nb_new_vertices = 0; std::vector non_manifold_cones; BOOST_FOREACH(halfedge_descriptor h, halfedges(tm)) { - if (halfedges_handled.insert(h).second) + // If 'h' is not visited yet, we walk around the target of 'h' and mark these + // halfedges as visited. Thus, if we are here and the target is already marked as visited, + // it means that the vertex is non manifold. + if(halfedges_handled.insert(h).second) { vertex_descriptor vd = target(h, tm); - if ( !vertices_handled.insert(vd).second ) + if(!vertices_handled.insert(vd).second) { put(cmap, vd, true); // store the originals non_manifold_cones.push_back(h); } else + { set_halfedge(vd, h, tm); - halfedge_descriptor start=opposite(next(h, tm), tm); - h=start; - do{ + } + + halfedge_descriptor start = opposite(next(h, tm), tm); + h = start; + do + { halfedges_handled.insert(h); - h=opposite(next(h, tm), tm); - }while(h!=start); + h = opposite(next(h, tm), tm); + } + while(h != start); } } - if (!non_manifold_cones.empty()) { + if(!non_manifold_cones.empty()) + { BOOST_FOREACH(halfedge_descriptor h, non_manifold_cones) { halfedge_descriptor start = h; @@ -1373,13 +1430,16 @@ std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, dmap.collect_vertices(target(h, tm), new_vd); put(vpm, new_vd, get(vpm, target(h, tm))); set_halfedge(new_vd, h, tm); - do{ + do + { set_target(h, new_vd, tm); - h=opposite(next(h, tm), tm); - } while(h!=start); + h = opposite(next(h, tm), tm); + } + while(h != start); } dmap.dump(out); } + return nb_new_vertices; } diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/data_degeneracies/caps_and_needles.off b/Polygon_mesh_processing/test/Polygon_mesh_processing/data_degeneracies/caps_and_needles.off new file mode 100644 index 00000000000..4e206746788 --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/data_degeneracies/caps_and_needles.off @@ -0,0 +1,17 @@ +OFF +9 3 0 +0 0 0 +1 0 0 +1 1 0 +0 0 1 +1 0 1 +10 10 1 +0 0 2 +1 0 2 +-0.99619469809 0.08715574274 2 +3 0 1 2 +3 3 4 5 +3 6 7 8 + + + diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp index 3bf2ffcd494..4dd421fe533 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp @@ -1,142 +1,248 @@ #include + #include -#include -#include + #include #include -typedef CGAL::Exact_predicates_inexact_constructions_kernel K; -typedef CGAL::Surface_mesh Surface_mesh; +#include + +#include +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef K::FT FT; +typedef K::Point_3 Point_3; +typedef CGAL::Surface_mesh Surface_mesh; void check_edge_degeneracy(const char* fname) { - std::ifstream input(fname); + std::cout << "test edge degeneracy..."; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + + std::ifstream input(fname); Surface_mesh mesh; if (!input || !(input >> mesh) || mesh.is_empty()) { std::cerr << fname << " is not a valid off file.\n"; - exit(1); + std::exit(1); } - typedef typename boost::graph_traits::edge_descriptor edge_descriptor; std::vector all_edges(edges(mesh).begin(), edges(mesh).end()); - CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[0], mesh)); - CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[1], mesh)); - CGAL_assertion(CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[2], mesh)); + assert(!CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[0], mesh)); + assert(!CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[1], mesh)); + assert(CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[2], mesh)); + std::cout << "done" << std::endl; } void check_triangle_face_degeneracy(const char* fname) { - std::ifstream input(fname); + std::cout << "test face degeneracy..."; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + std::ifstream input(fname); Surface_mesh mesh; if (!input || !(input >> mesh) || mesh.is_empty()) { std::cerr << fname << " is not a valid off file.\n"; - exit(1); + std::exit(1); } - typedef typename boost::graph_traits::face_descriptor face_descriptor; std::vector all_faces(faces(mesh).begin(), faces(mesh).end()); - CGAL_assertion(CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[0], mesh)); - CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[1], mesh)); - CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[2], mesh)); - CGAL_assertion(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[3], mesh)); + assert(CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[0], mesh)); + assert(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[1], mesh)); + assert(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[2], mesh)); + assert(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[3], mesh)); + + std::cout << "done" << std::endl; } -// tests repair.h -void test_vertices_merge_and_duplication(const char* fname) +// tests merge_and_duplication +template +void merge_identical_points(typename boost::graph_traits::vertex_descriptor v_keep, + typename boost::graph_traits::vertex_descriptor v_rm, + PolygonMesh& mesh) { - std::ifstream input(fname); - Surface_mesh mesh; - if (!input || !(input >> mesh) || mesh.is_empty()) { - std::cerr << fname << " is not a valid off file.\n"; - exit(1); + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + halfedge_descriptor h = halfedge(v_rm, mesh); + halfedge_descriptor start = h; + + do + { + set_target(h, v_keep, mesh); + h = opposite(next(h, mesh), mesh); } - const std::size_t initial_vertices = vertices(mesh).size(); + while( h != start ); - // create non-manifold vertex - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - std::vector all_vertices(vertices(mesh).begin(), vertices(mesh).end()); - CGAL::Polygon_mesh_processing::internal::merge_identical_points(mesh, all_vertices[1], all_vertices[7]); - - const std::size_t vertices_after_merge = vertices(mesh).size(); - CGAL_assertion(vertices_after_merge == initial_vertices - 1); - - std::vector< std::vector > duplicated_vertices; - CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices(mesh, - CGAL::parameters::output_iterator(std::back_inserter(duplicated_vertices))); - const std::size_t final_vertices_size = vertices(mesh).size(); - CGAL_assertion(final_vertices_size == vertices_after_merge + 1); - CGAL_assertion(final_vertices_size == initial_vertices); - CGAL_assertion(duplicated_vertices.size() == 2); + remove_vertex(v_rm, mesh); } void test_vertex_non_manifoldness(const char* fname) { + std::cout << "test vertex non manifoldness..."; + + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::vertices_size_type size_type; + std::ifstream input(fname); Surface_mesh mesh; if (!input || !(input >> mesh) || mesh.is_empty()) { std::cerr << fname << " is not a valid off file.\n"; - exit(1); + std::exit(1); } + size_type ini_nv = num_vertices(mesh); + // create non-manifold vertex - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - std::vector all_vertices(vertices(mesh).begin(), vertices(mesh).end()); - CGAL::Polygon_mesh_processing::internal::merge_identical_points(mesh, all_vertices[1], all_vertices[7]); - std::vector vertices_with_non_manifold(vertices(mesh).begin(), vertices(mesh).end()); - CGAL_assertion(vertices_with_non_manifold.size() == all_vertices.size() - 1); + Surface_mesh::Vertex_index vertex_to_merge_onto(1); + Surface_mesh::Vertex_index vertex_to_merge(7); + merge_identical_points(vertex_to_merge_onto, vertex_to_merge, mesh); + mesh.collect_garbage(); - BOOST_FOREACH(std::size_t iv, vertices(mesh)) + assert(num_vertices(mesh) == ini_nv - 1); + + BOOST_FOREACH(vertex_descriptor v, vertices(mesh)) { - vertex_descriptor v = vertices_with_non_manifold[iv]; - if(iv == 1) - CGAL_assertion(CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh)); + if(v == vertex_to_merge_onto) + assert(CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh)); else - CGAL_assertion(!CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh)); + assert(!CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh)); } + + std::cout << "done" << std::endl; } -void test_needle(const char* fname) +void test_vertices_merge_and_duplication(const char* fname) { + std::cout << "test non manifold vertex duplication..."; + + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + std::ifstream input(fname); Surface_mesh mesh; if (!input || !(input >> mesh) || mesh.is_empty()) { std::cerr << fname << " is not a valid off file.\n"; - exit(1); + std::exit(1); } + const std::size_t initial_vertices = num_vertices(mesh); - const double threshold = 0.8; - BOOST_FOREACH(typename boost::graph_traits::face_descriptor f, faces(mesh)) - { - CGAL_assertion(CGAL::Polygon_mesh_processing::is_needle_triangle_face(f, mesh, threshold)); - } + // create non-manifold vertex + Surface_mesh::Vertex_index vertex_to_merge_onto(1); + Surface_mesh::Vertex_index vertex_to_merge(7); + Surface_mesh::Vertex_index vertex_to_merge_2(14); + Surface_mesh::Vertex_index vertex_to_merge_3(21); + + Surface_mesh::Vertex_index vertex_to_merge_onto_2(2); + Surface_mesh::Vertex_index vertex_to_merge_4(8); + + merge_identical_points(vertex_to_merge_onto, vertex_to_merge, mesh); + merge_identical_points(vertex_to_merge_onto, vertex_to_merge_2, mesh); + merge_identical_points(vertex_to_merge_onto, vertex_to_merge_3, mesh); + merge_identical_points(vertex_to_merge_onto_2, vertex_to_merge_4, mesh); + mesh.collect_garbage(); + + const std::size_t vertices_after_merge = num_vertices(mesh); + assert(vertices_after_merge == initial_vertices - 4); + + std::vector > duplicated_vertices; + CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices(mesh, + CGAL::parameters::output_iterator(std::back_inserter(duplicated_vertices))); + + const std::size_t final_vertices_size = vertices(mesh).size(); + assert(final_vertices_size == initial_vertices); + assert(duplicated_vertices.size() == 2); // two non-manifold vertex + assert(duplicated_vertices.front().size() == 4); + assert(duplicated_vertices.back().size() == 2); + + std::cout << "done" << std::endl; } -void test_cap(const char* fname) +void test_needles_and_caps(const char* fname) { + std::cout << "test needles&caps..."; + + namespace PMP = CGAL::Polygon_mesh_processing; + + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::face_iterator face_iterator; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + std::ifstream input(fname); Surface_mesh mesh; if (!input || !(input >> mesh) || mesh.is_empty()) { std::cerr << fname << " is not a valid off file.\n"; - exit(1); + std::exit(1); } - const double threshold = -0.8; - BOOST_FOREACH(typename boost::graph_traits::face_descriptor f, faces(mesh)) - { - CGAL_assertion(CGAL::Polygon_mesh_processing::is_cap_triangle_face(f, mesh, threshold)); - } + const FT eps = std::numeric_limits::epsilon(); + + face_iterator fit, fend; + boost::tie(fit, fend) = faces(mesh); + + // (0 0 0) -- (1 0 0) -- (1 1 0) (90° cap angle) + face_descriptor f = *fit; + halfedge_descriptor res = PMP::is_needle_triangle_face(f, mesh, 2/*needle_threshold*/); + assert(res == boost::graph_traits::null_halfedge()); // not a needle + res = PMP::is_needle_triangle_face(f, mesh, CGAL::sqrt(FT(2) - eps)/*needle_threshold*/); + assert(res != boost::graph_traits::null_halfedge()); // is a needle + + res = PMP::is_cap_triangle_face(f, mesh, 0./*cos(pi/2)*/); + assert(mesh.point(target(res, mesh)) == CGAL::ORIGIN); + res = PMP::is_cap_triangle_face(f, mesh, std::cos(91 * CGAL_PI / 180)); + assert(res == boost::graph_traits::null_halfedge()); res = PMP::is_cap_triangle_face(f, mesh, std::cos(boost::math::constants::two_thirds_pi())); + assert(res == boost::graph_traits::null_halfedge()); + ++ fit; + + // (0 0 1) -- (1 0 1) -- (10 10 1) + f = *fit; + res = PMP::is_needle_triangle_face(f, mesh, 20); + assert(res == boost::graph_traits::null_halfedge()); + res = PMP::is_needle_triangle_face(f, mesh, 10 * CGAL::sqrt(FT(2) - eps)); + assert(mesh.point(target(res, mesh)) == Point_3(1,0,1)); + res = PMP::is_needle_triangle_face(f, mesh, 1); + assert(mesh.point(target(res, mesh)) == Point_3(1,0,1)); + + res = PMP::is_cap_triangle_face(f, mesh, 0./*cos(pi/2)*/); + assert(mesh.point(target(res, mesh)) == Point_3(0,0,1)); + res = PMP::is_cap_triangle_face(f, mesh, std::cos(boost::math::constants::two_thirds_pi())); + assert(mesh.point(target(res, mesh)) == Point_3(0,0,1)); + res = PMP::is_cap_triangle_face(f, mesh, std::cos(boost::math::constants::three_quarters_pi())); + assert(res == boost::graph_traits::null_halfedge()); + ++ fit; + + // (0 0 2) -- (1 0 2) -- (-0.99619469809 0.08715574274 2) (175° cap angle) + f = *fit; + res = PMP::is_needle_triangle_face(f, mesh, 2); + assert(res == boost::graph_traits::null_halfedge()); + res = PMP::is_needle_triangle_face(f, mesh, 1.9); + assert(mesh.point(target(res, mesh)) == Point_3(0,0,2) || + mesh.point(target(res, mesh)) == Point_3(1,0,2)); + res = PMP::is_needle_triangle_face(f, mesh, 1); + assert(mesh.point(target(res, mesh)) == Point_3(0,0,2) || + mesh.point(target(res, mesh)) == Point_3(1,0,2)); + + res = PMP::is_cap_triangle_face(f, mesh, 0./*cos(pi/2)*/); + assert(res != boost::graph_traits::null_halfedge() && + mesh.point(target(res, mesh)) != Point_3(0,0,2) && + mesh.point(target(res, mesh)) != Point_3(1,0,2)); + res = PMP::is_cap_triangle_face(f, mesh, std::cos(boost::math::constants::two_thirds_pi())); + assert(res != boost::graph_traits::null_halfedge()); + res = PMP::is_cap_triangle_face(f, mesh, std::cos(175 * CGAL_PI / 180)); + assert(res != boost::graph_traits::null_halfedge()); + res = PMP::is_cap_triangle_face(f, mesh, std::cos(176 * CGAL_PI / 180)); + assert(res == boost::graph_traits::null_halfedge()); + + std::cout << "done" << std::endl; } int main() { check_edge_degeneracy("data_degeneracies/degtri_edge.off"); check_triangle_face_degeneracy("data_degeneracies/degtri_four.off"); - test_vertices_merge_and_duplication("data_degeneracies/non_manifold_vertex_duplicated.off"); - test_vertex_non_manifoldness("data_degeneracies/non_manifold_vertex_duplicated.off"); - test_needle("data_degeneracies/needle.off"); - test_cap("data_degeneracies/cap.off"); + test_vertex_non_manifoldness("data/blobby.off"); + test_vertices_merge_and_duplication("data/blobby.off"); + test_needles_and_caps("data_degeneracies/caps_and_needles.off"); return 0; } diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Selection_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Selection_plugin.cpp index ac1bfd837d4..28d0d5f779a 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Selection_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Selection_plugin.cpp @@ -742,12 +742,10 @@ public Q_SLOTS: //Edition mode case 1: { - VPmap vpmap = get(CGAL::vertex_point, *selection_item->polyhedron()); bool is_valid = true; BOOST_FOREACH(boost::graph_traits::face_descriptor fd, faces(*selection_item->polyhedron())) { - if (CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(fd, - *selection_item->polyhedron())) + if (CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(fd, *selection_item->polyhedron())) { is_valid = false; break; diff --git a/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.cpp b/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.cpp index 266f5331b94..a71037f40f7 100644 --- a/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.cpp @@ -1682,7 +1682,7 @@ QString Scene_polyhedron_item::computeStats(int type) if (d->poly->is_pure_triangle()) { if (d->number_of_degenerated_faces == (unsigned int)(-1)) - d->number_of_degenerated_faces = nb_degenerate_faces(d->poly, get(CGAL::vertex_point, *(d->poly))); + d->number_of_degenerated_faces = nb_degenerate_faces(d->poly); return QString::number(d->number_of_degenerated_faces); } else diff --git a/Polyhedron/demo/Polyhedron/Scene_polyhedron_selection_item.cpp b/Polyhedron/demo/Polyhedron/Scene_polyhedron_selection_item.cpp index 488e874d416..6696a5ac9bc 100644 --- a/Polyhedron/demo/Polyhedron/Scene_polyhedron_selection_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_polyhedron_selection_item.cpp @@ -2032,8 +2032,9 @@ bool Scene_polyhedron_selection_item_priv::canAddFace(fg_halfedge_descriptor hc, found = true; fg_halfedge_descriptor res = CGAL::Euler::add_face_to_border(t,hc, *item->polyhedron()); + fg_face_descriptor resf = face(res, *item->polyhedron()); - if(CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(res, *item->polyhedron())) + if(CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(resf, *item->polyhedron())) { CGAL::Euler::remove_face(res, *item->polyhedron()); tempInstructions("Edge not selected : resulting facet is degenerated.", diff --git a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp index 75e60907f61..b2b56ff87d9 100644 --- a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp @@ -1502,7 +1502,7 @@ QString Scene_surface_mesh_item::computeStats(int type) if(is_triangle_mesh(*d->smesh_)) { if (d->number_of_degenerated_faces == (unsigned int)(-1)) - d->number_of_degenerated_faces = nb_degenerate_faces(d->smesh_, get(CGAL::vertex_point, *(d->smesh_))); + d->number_of_degenerated_faces = nb_degenerate_faces(d->smesh_); return QString::number(d->number_of_degenerated_faces); } else diff --git a/Polyhedron/demo/Polyhedron/include/CGAL/statistics_helpers.h b/Polyhedron/demo/Polyhedron/include/CGAL/statistics_helpers.h index 711a641dcc3..e65bdf7329b 100644 --- a/Polyhedron/demo/Polyhedron/include/CGAL/statistics_helpers.h +++ b/Polyhedron/demo/Polyhedron/include/CGAL/statistics_helpers.h @@ -1,8 +1,6 @@ #ifndef POLYHEDRON_DEMO_STATISTICS_HELPERS_H #define POLYHEDRON_DEMO_STATISTICS_HELPERS_H -#include - #include #include #include @@ -10,12 +8,15 @@ #include #include #include -#include #include #include -#include +#include +#include +#include +#include +#include template void angles(Mesh* poly, double& mini, double& maxi, double& ave) @@ -84,19 +85,15 @@ void edges_length(Mesh* poly, mid = extract_result< tag::median >(acc); } -template -unsigned int nb_degenerate_faces(Mesh* poly, VPmap vpmap) +template +unsigned int nb_degenerate_faces(Mesh* poly) { typedef typename boost::graph_traits::face_descriptor face_descriptor; - typedef typename CGAL::Kernel_traits< typename boost::property_traits::value_type >::Kernel Traits; - unsigned int nb = 0; - BOOST_FOREACH(face_descriptor f, faces(*poly)) - { - if (CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, *poly)) - ++nb; - } - return nb; + std::vector degenerate_faces; + CGAL::Polygon_mesh_processing::degenerate_faces(*poly, std::back_inserter(degenerate_faces)); + + return static_cast(degenerate_faces.size()); } template From 614f80694c6c95218560cce0717f2324fba669c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 23 Jul 2018 11:36:15 +0200 Subject: [PATCH 27/36] Removed obsolete code about merging duplicated boundary vertices --- .../merge_border_vertices.h | 37 ------------------- .../test_merging_border_vertices.cpp | 36 +----------------- .../test_predicates.cpp | 2 +- 3 files changed, 3 insertions(+), 72 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h index 2406c6d72de..c530a82b51d 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h @@ -345,35 +345,6 @@ void merge_duplicated_vertices_in_boundary_cycles( PolygonMesh& pm, merge_duplicated_vertices_in_boundary_cycle(h, pm, np); } -#if 0 -/// \ingroup PMP_repairing_grp -/// \todo document me -template -void merge_duplicated_boundary_vertices( PolygonMesh& pm, - const NamedParameter& np) -{ - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename GetVertexPointMap::const_type Vpm; - - Vpm vpm = choose_param(get_param(np, internal_np::vertex_point), - get_const_property_map(vertex_point, pm)); - - std::vector border_vertices; - BOOST_FOREACH(halfedge_descriptor h, halfedges(pm)) - { - if(is_border(h, pm)) - border_vertices.push_back(target(h, pm)); - } - - std::vector< std::vector > identical_vertices; - internal::detect_identical_vertices(border_vertices, identical_vertices, vpm); - - BOOST_FOREACH(const std::vector& vrtcs, identical_vertices) - merge_boundary_vertices(vrtcs, pm); -} -#endif - template void merge_duplicated_vertices_in_boundary_cycles(PolygonMesh& pm) { @@ -388,14 +359,6 @@ void merge_duplicated_vertices_in_boundary_cycle( merge_duplicated_vertices_in_boundary_cycle(h, pm, parameters::all_default()); } -#if 0 -template -void merge_duplicated_boundary_vertices(PolygonMesh& pm) -{ - merge_duplicated_boundary_vertices(pm, parameters::all_default()); -} -#endif - } } // end of CGAL::Polygon_mesh_processing #endif //CGAL_POLYGON_MESH_PROCESSING_MERGE_BORDER_VERTICES_H diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp index a3052bbd2fd..ed21427f4d1 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp @@ -38,49 +38,17 @@ void test_merge_duplicated_vertices_in_boundary_cycles(const char* fname, } } -#if 0 -void test_merge_duplicated_boundary_vertices(const char* fname, - std::size_t expected_nb_vertices) -{ - std::ifstream input(fname); - - Surface_mesh mesh; - if (!input || !(input >> mesh) || mesh.is_empty()) { - std::cerr << fname << " is not a valid off file.\n"; - exit(1); - } - - std::cout << "Testing merging globally " << fname << "\n"; - std::cout << " input mesh has " << vertices(mesh).size() << " vertices.\n"; - CGAL::Polygon_mesh_processing::merge_duplicated_boundary_vertices(mesh); - std::cout << " output mesh has " << vertices(mesh).size() << " vertices.\n"; - - assert(expected_nb_vertices == 0 || - expected_nb_vertices == vertices(mesh).size()); - if (expected_nb_vertices==0) - { - std::cout << "writting output to out2.off\n"; - std::ofstream output("out2.off"); - output << std::setprecision(17); - output << mesh; - } -} -#endif - int main(int argc, char** argv) { if (argc==1) { test_merge_duplicated_vertices_in_boundary_cycles("data/merge_points.off", 43); - // test_merge_duplicated_boundary_vertices("data/merge_points.off", 40); } else { for (int i=1; i< argc; ++i) - { test_merge_duplicated_vertices_in_boundary_cycles(argv[i], 0); - // test_merge_duplicated_boundary_vertices(argv[i], 0); - } } - return 0; + + return EXIT_SUCCESS; } diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp index 4dd421fe533..2124d1382ef 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp @@ -244,5 +244,5 @@ int main() test_vertices_merge_and_duplication("data/blobby.off"); test_needles_and_caps("data_degeneracies/caps_and_needles.off"); - return 0; + return EXIT_SUCCESS; } From a9897111c46092a5b820b6a90dc4fd3a3b935ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 23 Jul 2018 12:14:11 +0200 Subject: [PATCH 28/36] Reorganized the new functions --- .../Isotropic_remeshing/remesh_impl.h | 2 +- .../CGAL/Polygon_mesh_processing/repair.h | 41 +++++++++++++++-- .../{helpers.h => shape_predicates.h} | 46 ++++--------------- .../Polygon_mesh_processing/CMakeLists.txt | 2 +- ...edicates.cpp => test_shape_predicates.cpp} | 2 +- .../Plugins/PMP/Degenerated_faces_plugin.cpp | 4 +- .../Plugins/PMP/Selection_plugin.cpp | 2 +- .../Edit_polyhedron_plugin.cpp | 2 +- .../demo/Polyhedron/Scene_polyhedron_item.cpp | 2 +- .../Scene_polyhedron_selection_item.cpp | 2 +- .../Polyhedron/Scene_surface_mesh_item.cpp | 2 +- 11 files changed, 57 insertions(+), 50 deletions(-) rename Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/{helpers.h => shape_predicates.h} (91%) rename Polygon_mesh_processing/test/Polygon_mesh_processing/{test_predicates.cpp => test_shape_predicates.cpp} (99%) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index e5e914402db..5ffc9952be8 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h index 15c77eeb5c1..26986cb8153 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h @@ -41,7 +41,7 @@ #include #include -#include +#include #include #include @@ -146,6 +146,8 @@ namespace debug{ } } //end of namespace debug +namespace internal { + template struct Less_vertex_point{ typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; @@ -159,6 +161,8 @@ struct Less_vertex_point{ } }; +} // end namespace internal + template OutputIterator degenerate_faces(const TriangleMesh& tm, @@ -959,7 +963,7 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh, // preliminary step to check if the operation is possible // sort the boundary points along the common supporting line // we first need a reference point - typedef Less_vertex_point Less_vertex; + typedef internal::Less_vertex_point Less_vertex; std::pair< typename std::set::iterator, typename std::set::iterator > ref_vertices = @@ -1322,6 +1326,38 @@ struct Vertex_collector } // end namespace internal +/// \ingroup PMP_repairing_grp +/// checks whether a vertex of a triangle mesh is non-manifold. +/// +/// @tparam TriangleMesh a model of `HalfedgeListGraph` +/// +/// @param v a vertex of `tm` +/// @param tm a triangle mesh containing `v` +/// +/// \return `true` if the vertex is non-manifold, `false` otherwise. +template +bool is_non_manifold_vertex(typename boost::graph_traits::vertex_descriptor v, + const TriangleMesh& tm) +{ + CGAL_assertion(CGAL::is_triangle_mesh(tm)); + + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + boost::unordered_set halfedges_handled; + BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(v, tm)) + halfedges_handled.insert(h); + + BOOST_FOREACH(halfedge_descriptor h, halfedges(tm)) + { + if(v == target(h, tm)) + { + if(halfedges_handled.count(h) == 0) + return true; + } + } + return false; +} + /// \ingroup PMP_repairing_grp /// duplicates all non-manifold vertices of the input mesh. /// @@ -1449,7 +1485,6 @@ std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm) return duplicate_non_manifold_vertices(tm, parameters::all_default()); } - /// \ingroup PMP_repairing_grp /// removes the isolated vertices from any polygon mesh. /// A vertex is considered isolated if it is not incident to any simplex diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/shape_predicates.h similarity index 91% rename from Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h rename to Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/shape_predicates.h index 8e9cfeef182..dc66aecacf9 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/helpers.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/shape_predicates.h @@ -20,8 +20,8 @@ // Author(s) : Konstantinos Katrioplas, // Mael Rouxel-Labbé -#ifndef CGAL_POLYGON_MESH_PROCESSING_HELPERS_H -#define CGAL_POLYGON_MESH_PROCESSING_HELPERS_H +#ifndef CGAL_POLYGON_MESH_PROCESSING_SHAPE_PREDICATES_H +#define CGAL_POLYGON_MESH_PROCESSING_SHAPE_PREDICATES_H #include #include @@ -42,38 +42,6 @@ namespace CGAL { namespace Polygon_mesh_processing { -/// \ingroup PMP_repairing_grp -/// checks whether a vertex of a triangle mesh is non-manifold. -/// -/// @tparam TriangleMesh a model of `HalfedgeListGraph` -/// -/// @param v a vertex of `tm` -/// @param tm a triangle mesh containing `v` -/// -/// \return `true` if the vertrex is non-manifold, `false` otherwise. -template -bool is_non_manifold_vertex(typename boost::graph_traits::vertex_descriptor v, - const TriangleMesh& tm) -{ - CGAL_assertion(CGAL::is_triangle_mesh(tm)); - - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - - boost::unordered_set halfedges_handled; - BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(v, tm)) - halfedges_handled.insert(h); - - BOOST_FOREACH(halfedge_descriptor h, halfedges(tm)) - { - if(v == target(h, tm)) - { - if(halfedges_handled.count(h) == 0) - return true; - } - } - return false; -} - /// \ingroup PMP_repairing_grp /// checks whether an edge is degenerate. /// An edge is considered degenerate if the geometric positions of its two extremities are identical. @@ -202,7 +170,7 @@ bool is_degenerate_triangle_face(typename boost::graph_traits::fac /// \cgalParamEnd /// \cgalNamedParamsEnd /// -/// \return the smallest halfedge if the triangle face is a needle, and a null halfedge otherwise. +/// \return the shortest halfedge if the triangle face is a needle, and a null halfedge otherwise. template typename boost::graph_traits::halfedge_descriptor is_needle_triangle_face(typename boost::graph_traits::face_descriptor f, @@ -287,11 +255,13 @@ is_needle_triangle_face(typename boost::graph_traits::face_descrip /// `CGAL::vertex_point_t` should be available in `TriangleMesh` /// \cgalParamEnd /// \cgalParamBegin{geom_traits} a geometric traits class instance. -/// The traits class must provide the nested type `Point_3` +/// The traits class must provide the nested type `Point_3` and +/// the nested functors `Compute_squared_distance_3`, `Construct_vector_3`, +/// and `Compute_scalar_product_3`. /// \cgalParamEnd /// \cgalNamedParamsEnd /// -/// \return `true` if the triangle face is a cap +/// \return the halfedge opposite of the largest angle if the face is a cap, and a null halfedge otherwise. template typename boost::graph_traits::halfedge_descriptor is_cap_triangle_face(typename boost::graph_traits::face_descriptor f, @@ -361,4 +331,4 @@ is_cap_triangle_face(typename boost::graph_traits::face_descriptor } } // end namespaces CGAL and PMP -#endif // CGAL_POLYGON_MESH_PROCESSING_HELPERS_H +#endif // CGAL_POLYGON_MESH_PROCESSING_SHAPE_PREDICATES_H diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt index 0c26363fca7..d8436cb8f30 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt @@ -102,7 +102,7 @@ endif() create_single_source_cgal_program("test_pmp_transform.cpp") create_single_source_cgal_program("remove_degeneracies_test.cpp") create_single_source_cgal_program("test_merging_border_vertices.cpp") - create_single_source_cgal_program("test_predicates.cpp") + create_single_source_cgal_program("test_shape_predicates.cpp") if( TBB_FOUND ) CGAL_target_use_TBB(test_pmp_distance) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_shape_predicates.cpp similarity index 99% rename from Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp rename to Polygon_mesh_processing/test/Polygon_mesh_processing/test_shape_predicates.cpp index 2124d1382ef..7526ac80bec 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_predicates.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_shape_predicates.cpp @@ -2,8 +2,8 @@ #include -#include #include +#include #include diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Degenerated_faces_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Degenerated_faces_plugin.cpp index b7c359e9ab1..9df12173d1f 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Degenerated_faces_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Degenerated_faces_plugin.cpp @@ -19,7 +19,9 @@ #include #include #include -#include + +#include + #ifdef USE_SURFACE_MESH typedef Scene_surface_mesh_item Scene_facegraph_item; #else diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Selection_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Selection_plugin.cpp index 28d0d5f779a..0b471fddc9b 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Selection_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Selection_plugin.cpp @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include #ifdef USE_SURFACE_MESH diff --git a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Edit_polyhedron_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Edit_polyhedron_plugin.cpp index 18a5d1bb8e0..d0dab116457 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Edit_polyhedron_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh_deformation/Edit_polyhedron_plugin.cpp @@ -9,7 +9,7 @@ #include "Scene_edit_polyhedron_item.h" #include "Scene_polyhedron_selection_item.h" #include -#include +#include #include #include #include diff --git a/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.cpp b/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.cpp index a71037f40f7..df752a67272 100644 --- a/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_polyhedron_item.cpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/Polyhedron/demo/Polyhedron/Scene_polyhedron_selection_item.cpp b/Polyhedron/demo/Polyhedron/Scene_polyhedron_selection_item.cpp index 6696a5ac9bc..d3d65dcd44c 100644 --- a/Polyhedron/demo/Polyhedron/Scene_polyhedron_selection_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_polyhedron_selection_item.cpp @@ -2,7 +2,7 @@ #include "Scene_polyhedron_selection_item.h" #include #include -#include +#include #include #include #include diff --git a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp index b2b56ff87d9..5b790daed6f 100644 --- a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include #include #include From 3934ad351e3de218049b7887ff6f0f9aa7bd6be9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 23 Jul 2018 16:03:05 +0200 Subject: [PATCH 29/36] Fixed BGL concepts --- BGL/doc/BGL/Concepts/EdgeListGraph.h | 8 ++++---- BGL/doc/BGL/PackageDescription.txt | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/BGL/doc/BGL/Concepts/EdgeListGraph.h b/BGL/doc/BGL/Concepts/EdgeListGraph.h index 7d7980aa172..d763d3a01fe 100644 --- a/BGL/doc/BGL/Concepts/EdgeListGraph.h +++ b/BGL/doc/BGL/Concepts/EdgeListGraph.h @@ -38,16 +38,16 @@ num_edges(const EdgeListGraph& g); /*! \relates EdgeListGraph -returns the source vertex of `h`. +returns the source vertex of `e`. */ template boost::graph_traits::vertex_descriptor -source(boost::graph_traits::halfedge_descriptor h, const EdgeListGraph& g); +source(boost::graph_traits::edge_descriptor e, const EdgeListGraph& g); /*! \relates EdgeListGraph -returns the target vertex of `h`. +returns the target vertex of `e`. */ template boost::graph_traits::vertex_descriptor -target(boost::graph_traits::halfedge_descriptor h, const EdgeListGraph& g); +target(boost::graph_traits::edge_descriptor e, const EdgeListGraph& g); diff --git a/BGL/doc/BGL/PackageDescription.txt b/BGL/doc/BGL/PackageDescription.txt index d40b1ec6ecf..86efff4c7a8 100644 --- a/BGL/doc/BGL/PackageDescription.txt +++ b/BGL/doc/BGL/PackageDescription.txt @@ -122,12 +122,12 @@ and adds the requirement for traversal of all edges in a graph. An upper bound of the number of edges of the graph - `source(g)` + `source(e, g)` `vertex_descriptor` The source vertex of `e` - `target(g)` + `target(e, g)` `vertex_descriptor` The target vertex of `e` From e3da86cff373a63ae8d840b22a79e346cc37c13d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 23 Jul 2018 16:07:33 +0200 Subject: [PATCH 30/36] Renamed removed_(null-->degenerate)_edges() for consistency --- .../CGAL/Polygon_mesh_processing/repair.h | 46 +++++++++---------- 1 file changed, 22 insertions(+), 24 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h index 26986cb8153..7323c9b73db 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h @@ -365,10 +365,9 @@ remove_a_border_edge(typename boost::graph_traits::edge_descriptor } template -std::size_t remove_null_edges( - const EdgeRange& edge_range, - TriangleMesh& tmesh, - const NamedParameters& np) +std::size_t remove_degenerate_edges(const EdgeRange& edge_range, + TriangleMesh& tmesh, + const NamedParameters& np) { CGAL_assertion(CGAL::is_triangle_mesh(tmesh)); @@ -385,27 +384,27 @@ std::size_t remove_null_edges( typedef typename GetVertexPointMap::type VertexPointMap; VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point), get_property_map(vertex_point, tmesh)); + typedef typename GetGeomTraits::type Traits; - Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits()); std::size_t nb_deg_faces = 0; // collect edges of length 0 - std::set null_edges_to_remove; + std::set degenerate_edges_to_remove; BOOST_FOREACH(edge_descriptor ed, edge_range) { - if ( traits.equal_3_object()(get(vpmap, target(ed, tmesh)), get(vpmap, source(ed, tmesh))) ) - null_edges_to_remove.insert(ed); + if(is_degenerate_edge(ed, tmesh, np)) + degenerate_edges_to_remove.insert(ed); } #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << "Found " << null_edges_to_remove.size() << " null edges.\n"; + std::cout << "Found " << degenerate_edges_to_remove.size() << " null edges.\n"; #endif - while (!null_edges_to_remove.empty()) + while (!degenerate_edges_to_remove.empty()) { - edge_descriptor ed = *null_edges_to_remove.begin(); - null_edges_to_remove.erase(null_edges_to_remove.begin()); + edge_descriptor ed = *degenerate_edges_to_remove.begin(); + degenerate_edges_to_remove.erase(degenerate_edges_to_remove.begin()); halfedge_descriptor h = halfedge(ed, tmesh); @@ -415,12 +414,12 @@ std::size_t remove_null_edges( if ( face(h, tmesh)!=GT::null_face() ) { ++nb_deg_faces; - null_edges_to_remove.erase(edge(prev(h, tmesh), tmesh)); + degenerate_edges_to_remove.erase(edge(prev(h, tmesh), tmesh)); } if (face(opposite(h, tmesh), tmesh)!=GT::null_face()) { ++nb_deg_faces; - null_edges_to_remove.erase(edge(prev(opposite(h, tmesh), tmesh), tmesh)); + degenerate_edges_to_remove.erase(edge(prev(opposite(h, tmesh), tmesh), tmesh)); } //now remove the edge CGAL::Euler::collapse_edge(ed, tmesh); @@ -435,7 +434,7 @@ std::size_t remove_null_edges( if (is_triangle(hd, tmesh)) { Euler::fill_hole(hd, tmesh); - null_edges_to_remove.insert(ed); + degenerate_edges_to_remove.insert(ed); continue; } } @@ -621,7 +620,7 @@ std::size_t remove_null_edges( // remove edges BOOST_FOREACH(edge_descriptor ed, edges_to_remove) { - null_edges_to_remove.erase(ed); + degenerate_edges_to_remove.erase(ed); remove_edge(ed, tmesh); } @@ -641,8 +640,8 @@ std::size_t remove_null_edges( put(vpmap, target(new_hd, tmesh), pt); BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(new_hd, tmesh)) - if ( traits.equal_3_object()(get(vpmap, target(hd, tmesh)), get(vpmap, source(hd, tmesh))) ) - null_edges_to_remove.insert(edge(hd, tmesh)); + if(is_degenerate_edge(edge(hd, tmesh), tmesh, np)) + degenerate_edges_to_remove.insert(edge(hd, tmesh)); CGAL_assertion( is_valid_polygon_mesh(tmesh) ); } @@ -652,12 +651,10 @@ std::size_t remove_null_edges( } template -std::size_t remove_null_edges( - const EdgeRange& edge_range, - TriangleMesh& tmesh) +std::size_t remove_degenerate_edges(const EdgeRange& edge_range, + TriangleMesh& tmesh) { - return remove_null_edges(edge_range, tmesh, - parameters::all_default()); + return remove_degenerate_edges(edge_range, tmesh, parameters::all_default()); } /// \ingroup PMP_repairing_grp @@ -717,8 +714,9 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh, typedef typename boost::property_traits::value_type Point_3; typedef typename boost::property_traits::reference Point_ref; + // First remove edges of length 0 - std::size_t nb_deg_faces = remove_null_edges(edges(tmesh), tmesh, np); + std::size_t nb_deg_faces = remove_degenerate_edges(edges(tmesh), tmesh, np); #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG { From 31609f2002f379a49291e8d1e498eec8e7b0771d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 23 Jul 2018 16:31:51 +0200 Subject: [PATCH 31/36] Renamed function and removed obsolete code --- .../merge_border_vertices.h | 52 ++----------------- 1 file changed, 4 insertions(+), 48 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h index c530a82b51d..764022e0a6e 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h @@ -37,48 +37,6 @@ namespace Polygon_mesh_processing{ namespace internal { -#if 0 -// warning: vertices will be altered (sorted) -template -void detect_identical_vertices(std::vector& vertices, - std::vector< std::vector >& identical_vertices, - Vpm vpm) -{ - typedef typename boost::property_traits::value_type Point_3; - - // sort vertices using their point to ease the detection - // of vertices with identical points - CGAL::Property_map_to_unary_function Get_point(vpm); - std::sort( vertices.begin(), vertices.end(), - boost::bind(std::less(), boost::bind(Get_point,_1), - boost::bind(Get_point, _2)) ); - std::size_t nbv=vertices.size(); - std::size_t i=1; - - while(i!=nbv) - { - if (get(vpm, vertices[i]) == get(vpm, vertices[i-1])) - { - identical_vertices.push_back( std::vector() ); - identical_vertices.back().push_back(vertices[i-1]); - identical_vertices.back().push_back(vertices[i]); - while(++i!=nbv) - { - if (get(vpm, vertices[i]) == get(vpm, vertices[i-1])) - identical_vertices.back().push_back(vertices[i]); - else - { - ++i; - break; - } - } - } - else - ++i; - } -} -#endif - template struct Less_on_point_of_target { @@ -188,7 +146,6 @@ void detect_identical_mergeable_vertices( /// @param pm the polygon mesh. /// @param out an output iterator where the list of halfedges will be put. /// -/// @todo Maybe move to BGL /// @todo It should make sense to also return the length of each cycle. /// @todo It should probably go into BGL package. template @@ -221,17 +178,16 @@ extract_boundary_cycles(PolygonMesh& pm, /// @param sorted_hedges a sorted list of halfedges. /// @param pm the polygon mesh which contains the list of halfedges. /// -/// @todo rename me to `merge_vertices_in_range` because I merge any king of vertices in the list. template -void merge_boundary_vertices_in_cycle(const HalfedgeRange& sorted_hedges, - PolygonMesh& pm) +void merge_vertices_in_range(const HalfedgeRange& sorted_hedges, + PolygonMesh& pm) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; halfedge_descriptor in_h_kept = *boost::begin(sorted_hedges); halfedge_descriptor out_h_kept = next(in_h_kept, pm); - vertex_descriptor v_kept=target(in_h_kept, pm); + vertex_descriptor v_kept = target(in_h_kept, pm); std::vector vertices_to_rm; @@ -311,7 +267,7 @@ void merge_duplicated_vertices_in_boundary_cycle( { start=hedges.front(); // hedges are sorted along the cycle - merge_boundary_vertices_in_cycle(hedges, pm); + merge_vertices_in_range(hedges, pm); } } From 018195d15df358c59a4ac9a613ce00c83c0fab20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 23 Jul 2018 17:09:56 +0200 Subject: [PATCH 32/36] Documented 'degenerate_faces' and add 'degenerate_edges' --- .../CGAL/Polygon_mesh_processing/repair.h | 159 +++++++++++++++--- 1 file changed, 137 insertions(+), 22 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h index 7323c9b73db..f41ce6aa276 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h @@ -24,10 +24,6 @@ #include -#include -#include - -#include #include #include #include @@ -46,17 +42,28 @@ #include #include +#include + #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG #include #include -#include -#include #endif +#include +#include +#include + +#include +#include +#include +#include +#include +#include + namespace CGAL{ namespace Polygon_mesh_processing { - namespace debug{ + template std::ostream& dump_edge_neighborhood( typename boost::graph_traits::edge_descriptor ed, @@ -144,6 +151,7 @@ namespace debug{ << vids[ target(next(next(halfedge(f, tm), tm), tm), tm) ] << "\n"; } } + } //end of namespace debug namespace internal { @@ -163,15 +171,104 @@ struct Less_vertex_point{ } // end namespace internal +/// \ingroup PMP_repairing_grp +/// collects the degenerate edges within a given range of edges. +/// +/// @tparam EdgeRange a model of `Range` with value type `boost::graph_traits::edge_descriptor` +/// @tparam TriangleMesh a model of `EdgeListGraph` +/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" +/// +/// @param edges a subset of edges of `tm` +/// @param tm a triangle mesh +/// @param np optional \ref pmp_namedparameters "Named Parameters" described below +/// +/// \cgalNamedParamsBegin +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`. +/// The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `TriangleMesh` +/// \cgalParamEnd +/// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// The traits class must provide the nested type `Point_3`, +/// and the nested functor `Equal_3` to check whether two points are identical. +/// \cgalParamEnd +/// \cgalNamedParamsEnd +template +OutputIterator degenerate_edges(const EdgeRange& edges, + const TriangleMesh& tm, + OutputIterator out, + const NamedParameters& np) +{ + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + + BOOST_FOREACH(edge_descriptor ed, edges) + { + if(is_degenerate_edge(ed, tm, np)) + *out++ = ed; + } + return out; +} + +template +OutputIterator degenerate_edges(const EdgeRange& edges, + const TriangleMesh& tm, + OutputIterator out, + typename boost::disable_if_c< + CGAL::is_iterator::value + >::type* = 0) +{ + return degenerate_edges(edges, tm, out, CGAL::parameters::all_default()); +} + template +OutputIterator degenerate_edges(const TriangleMesh& tm, + OutputIterator out, + const NamedParameters& np, + typename boost::enable_if_c< + CGAL::is_iterator::value + >::type* = 0) +{ + return degenerate_edges(edges(tm), tm, out, np); +} + +template OutputIterator -degenerate_faces(const TriangleMesh& tm, - OutputIterator out, - const NamedParameters& np) +degenerate_edges(const TriangleMesh& tm, OutputIterator out) +{ + return degenerate_edges(edges(tm), tm, out, CGAL::parameters::all_default()); +} + +/// \ingroup PMP_repairing_grp +/// collects the degenerate faces within a given range of faces. +/// +/// @tparam FaceRange a model of `Range` with value type `boost::graph_traits::face_descriptor` +/// @tparam TriangleMesh a model of `FaceGraph` +/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" +/// +/// @param faces a subset of faces of `tm` +/// @param tm a triangle mesh +/// @param np optional \ref pmp_namedparameters "Named Parameters" described below +/// +/// \cgalNamedParamsBegin +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`. +/// The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` should be available in `TriangleMesh` +/// \cgalParamEnd +/// \cgalParamBegin{geom_traits} a geometric traits class instance. +/// The traits class must provide the nested functor `Collinear_3` +/// to check whether three points are collinear. +/// \cgalParamEnd +/// \cgalNamedParamsEnd +template +OutputIterator degenerate_faces(const FaceRange& faces, + const TriangleMesh& tm, + OutputIterator out, + const NamedParameters& np) { typedef typename boost::graph_traits::face_descriptor face_descriptor; - BOOST_FOREACH(face_descriptor fd, faces(tm)) + BOOST_FOREACH(face_descriptor fd, faces) { if(is_degenerate_triangle_face(fd, tm, np)) *out++ = fd; @@ -179,11 +276,32 @@ degenerate_faces(const TriangleMesh& tm, return out; } -template -OutputIterator -degenerate_faces(const TriangleMesh& tm, OutputIterator out) +template +OutputIterator degenerate_faces(const FaceRange& faces, + const TriangleMesh& tm, + OutputIterator out, + typename boost::disable_if_c< + CGAL::is_iterator::value + >::type* = 0) { - return degenerate_faces(tm, out, CGAL::parameters::all_default()); + return degenerate_faces(faces, tm, out, CGAL::parameters::all_default()); +} + +template +OutputIterator degenerate_faces(const TriangleMesh& tm, + OutputIterator out, + const NamedParameters& np, + typename boost::enable_if_c< + CGAL::is_iterator::value + >::type* = 0) +{ + return degenerate_faces(faces(tm), tm, out, np); +} + +template +OutputIterator degenerate_faces(const TriangleMesh& tm, OutputIterator out) +{ + return degenerate_faces(faces(tm), tm, out, CGAL::parameters::all_default()); } // this function remove a border edge even if it does not satisfy the link condition. @@ -391,15 +509,12 @@ std::size_t remove_degenerate_edges(const EdgeRange& edge_range, // collect edges of length 0 std::set degenerate_edges_to_remove; - BOOST_FOREACH(edge_descriptor ed, edge_range) - { - if(is_degenerate_edge(ed, tmesh, np)) - degenerate_edges_to_remove.insert(ed); - } + degenerate_edges(edge_range, tmesh, std::inserter(degenerate_edges_to_remove, + degenerate_edges_to_remove.end())); - #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG std::cout << "Found " << degenerate_edges_to_remove.size() << " null edges.\n"; - #endif +#endif while (!degenerate_edges_to_remove.empty()) { From 3d0c0d48d4f74c6a5758e68c1c8e8e6b9c72bde9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 23 Jul 2018 17:28:36 +0200 Subject: [PATCH 33/36] Minor doc changes --- .../include/CGAL/Polygon_mesh_processing/repair.h | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h index f41ce6aa276..4e1fe05fa1e 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair.h @@ -786,15 +786,15 @@ std::size_t remove_degenerate_edges(const EdgeRange& edge_range, /// @param np optional \ref pmp_namedparameters "Named Parameters" described below /// /// \cgalNamedParamsBegin -/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`. -/// If this parameter is omitted, an internal property map for -/// `CGAL::vertex_point_t` must be available in `TriangleMesh` -/// \cgalParamEnd +/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. +/// The type of this map is model of `ReadWritePropertyMap`. +/// If this parameter is omitted, an internal property map for +/// `CGAL::vertex_point_t` must be available in `TriangleMesh` +/// \cgalParamEnd /// \cgalParamBegin{geom_traits} a geometric traits class instance. /// The traits class must provide the nested type `Point_3`, /// and the nested functors : /// - `Compare_distance_3` to compute the distance between 2 points -/// - `Collinear_are_ordered_along_line_3` to check whether 3 collinear points are ordered /// - `Collinear_3` to check whether 3 points are collinear /// - `Less_xyz_3` to compare lexicographically two points /// - `Equal_3` to check whether 2 points are identical @@ -804,6 +804,7 @@ std::size_t remove_degenerate_edges(const EdgeRange& edge_range, /// /// \todo the function might not be able to remove all degenerate faces. /// We should probably do something with the return type. +/// /// \return number of removed degenerate faces template std::size_t remove_degenerate_faces(TriangleMesh& tmesh, @@ -1472,7 +1473,7 @@ bool is_non_manifold_vertex(typename boost::graph_traits::vertex_d } /// \ingroup PMP_repairing_grp -/// duplicates all non-manifold vertices of the input mesh. +/// duplicates all the non-manifold vertices of the input mesh. /// /// @tparam TriangleMesh a model of `HalfedgeListGraph` and `MutableHalfedgeGraph` /// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" From 6c0d6a79eb9372f7b1378cfe302e96f8055bc0ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 23 Jul 2018 17:28:44 +0200 Subject: [PATCH 34/36] Test degenerate_edges/faces --- .../remove_degeneracies_test.cpp | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp index 8b6488320e1..b37cc61eb92 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp @@ -11,11 +11,34 @@ //the last test (on trihole.off) does not terminate // -typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +namespace PMP = CGAL::Polygon_mesh_processing; -typedef CGAL::Surface_mesh Surface_mesh; +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; -void fix(const char* fname) +typedef CGAL::Surface_mesh Surface_mesh; + +typedef boost::graph_traits::edge_descriptor edge_descriptor; +typedef boost::graph_traits::face_descriptor face_descriptor; + +void detect_degeneracies(const Surface_mesh& mesh) +{ + std::vector dfaces; + + PMP::degenerate_faces(mesh, std::back_inserter(dfaces)); + PMP::degenerate_faces(faces(mesh), mesh, std::back_inserter(dfaces)); + PMP::degenerate_faces(mesh, std::back_inserter(dfaces), CGAL::parameters::all_default()); + PMP::degenerate_faces(faces(mesh), mesh, std::back_inserter(dfaces), CGAL::parameters::all_default()); + assert(!dfaces.empty()); + + std::set dedges; + PMP::degenerate_edges(mesh, std::inserter(dedges, dedges.end())); + PMP::degenerate_edges(edges(mesh), mesh, std::inserter(dedges, dedges.begin())); + PMP::degenerate_edges(mesh, std::inserter(dedges, dedges.end()), CGAL::parameters::all_default()); + PMP::degenerate_edges(edges(mesh), mesh, std::inserter(dedges, dedges.begin()), CGAL::parameters::all_default()); + assert(dedges.empty()); +} + +void fix_degeneracies(const char* fname) { std::ifstream input(fname); @@ -24,21 +47,22 @@ void fix(const char* fname) std::cerr << fname << " is not a valid off file.\n"; exit(1); } - CGAL::Polygon_mesh_processing::remove_degenerate_faces(mesh); + detect_degeneracies(mesh); + + CGAL::Polygon_mesh_processing::remove_degenerate_faces(mesh); assert( CGAL::is_valid_polygon_mesh(mesh) ); } - int main() { - fix("data_degeneracies/degtri_2dt_1edge_split_twice.off"); - fix("data_degeneracies/degtri_four-2.off"); - fix("data_degeneracies/degtri_four.off"); - fix("data_degeneracies/degtri_on_border.off"); - fix("data_degeneracies/degtri_three.off"); - fix("data_degeneracies/degtri_single.off"); - fix("data_degeneracies/trihole.off"); + fix_degeneracies("data_degeneracies/degtri_2dt_1edge_split_twice.off"); + fix_degeneracies("data_degeneracies/degtri_four-2.off"); + fix_degeneracies("data_degeneracies/degtri_four.off"); + fix_degeneracies("data_degeneracies/degtri_on_border.off"); + fix_degeneracies("data_degeneracies/degtri_three.off"); + fix_degeneracies("data_degeneracies/degtri_single.off"); + fix_degeneracies("data_degeneracies/trihole.off"); return 0; } From 9bf6c331b946a2ab877b2614408ff16101329888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 23 Jul 2018 17:42:11 +0200 Subject: [PATCH 35/36] Moved extract_boundary_cycles to border.h --- .../CGAL/Polygon_mesh_processing/border.h | 40 +++++++++++++++++-- .../merge_border_vertices.h | 36 ++--------------- 2 files changed, 39 insertions(+), 37 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/border.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/border.h index 6c30cb6d077..dbe1f4945a7 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/border.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/border.h @@ -24,16 +24,16 @@ #include +#include +#include +#include +#include #include #include #include #include -#include -#include -#include - #include namespace CGAL{ @@ -257,6 +257,38 @@ namespace Polygon_mesh_processing { return border_counter; } + + /// @ingroup PkgPolygonMeshProcessing + /// extracts boundary cycles as a list of halfedges, with one halfedge per border. + /// + /// @tparam PolygonMesh a model of `HalfedgeListGraph` + /// @tparam OutputIterator a model of `OutputIterator` holding objects of type + /// `boost::graph_traits::%halfedge_descriptor` + /// + /// @param pm a polygon mesh + /// @param out an output iterator where the border halfedges will be put + /// + /// @todo It could make sense to also return the length of each cycle. + /// @todo It should probably go into BGL package (like the rest of this file). + template + OutputIterator extract_boundary_cycles(PolygonMesh& pm, + OutputIterator out) + { + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + boost::unordered_set hedge_handled; + BOOST_FOREACH(halfedge_descriptor h, halfedges(pm)) + { + if(is_border(h, pm) && hedge_handled.insert(h).second) + { + *out++ = h; + BOOST_FOREACH(halfedge_descriptor h2, halfedges_around_face(h, pm)) + hedge_handled.insert(h2); + } + } + return out; + } + } // end of namespace Polygon_mesh_processing } // end of namespace CGAL diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h index 764022e0a6e..e58eae9d3da 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -31,9 +32,9 @@ #include #include -namespace CGAL{ +namespace CGAL { -namespace Polygon_mesh_processing{ +namespace Polygon_mesh_processing { namespace internal { @@ -137,37 +138,6 @@ void detect_identical_mergeable_vertices( } // end of internal -/// \ingroup PMP_repairing_grp -/// extracts boundary cycles as a list of halfedges. -/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph`. -/// @tparam OutputIterator a model of `OutputIterator` holding objects of type -/// `boost::graph_traits::%halfedge_descriptor` -/// -/// @param pm the polygon mesh. -/// @param out an output iterator where the list of halfedges will be put. -/// -/// @todo It should make sense to also return the length of each cycle. -/// @todo It should probably go into BGL package. -template -OutputIterator -extract_boundary_cycles(PolygonMesh& pm, - OutputIterator out) -{ - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - - boost::unordered_set hedge_handled; - BOOST_FOREACH(halfedge_descriptor h, halfedges(pm)) - { - if(is_border(h, pm) && hedge_handled.insert(h).second) - { - *out++=h; - BOOST_FOREACH(halfedge_descriptor h2, halfedges_around_face(h, pm)) - hedge_handled.insert(h2); - } - } - return out; -} - /// \ingroup PMP_repairing_grp /// merges target vertices of a list of halfedges. /// Halfedges must be sorted in the list. From 5db403ae343aabf2e227d30d50f18ac7196faeb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 23 Jul 2018 17:44:04 +0200 Subject: [PATCH 36/36] Fixed indentation --- .../CGAL/Polygon_mesh_processing/merge_border_vertices.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h index e58eae9d3da..6f0185533ef 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h @@ -71,8 +71,8 @@ template void detect_identical_mergeable_vertices( std::vector< std::pair >& cycle_hedges, std::vector< std::vector >& hedges_with_identical_point_target, - const PolygonMesh& pm, - Vpm vpm) + const PolygonMesh& pm, + Vpm vpm) { // sort vertices using their point to ease the detection // of vertices with identical points @@ -211,7 +211,7 @@ template void merge_duplicated_vertices_in_boundary_cycle( typename boost::graph_traits::halfedge_descriptor h, PolygonMesh& pm, - const NamedParameter& np) + const NamedParameter& np) { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename GetVertexPointMap::const_type Vpm;