diff --git a/BGL/doc/BGL/PackageDescription.txt b/BGL/doc/BGL/PackageDescription.txt index d1a8e72faf0..b336b89a6db 100644 --- a/BGL/doc/BGL/PackageDescription.txt +++ b/BGL/doc/BGL/PackageDescription.txt @@ -150,6 +150,8 @@ user might encounter. - `CGAL::make_quad()` - `CGAL::make_hexahedron()` +- `CGAL::clear()` + ## Iterators ## - `CGAL::Halfedge_around_source_iterator` - `CGAL::Halfedge_around_target_iterator` diff --git a/BGL/include/CGAL/boost/graph/graph_traits_PolyMesh_ArrayKernelT.h b/BGL/include/CGAL/boost/graph/graph_traits_PolyMesh_ArrayKernelT.h index 7742bc729e8..ff8ddf22cf6 100644 --- a/BGL/include/CGAL/boost/graph/graph_traits_PolyMesh_ArrayKernelT.h +++ b/BGL/include/CGAL/boost/graph/graph_traits_PolyMesh_ArrayKernelT.h @@ -654,6 +654,25 @@ bool is_valid(OpenMesh::PolyMesh_ArrayKernelT& sm, bool /* verbose */ = false } // namespace OpenMesh +namespace CGAL { + +// Overload CGAL::clear function. PolyMesh_ArrayKernel behaves +// differently from other meshes. Calling clear does not affect the +// number of vertices, edges, or faces in the mesh. To get actual +// numbers it is necessary to first collect garbage. We add an +// overlaod to get consistent behavior. +template +void clear(OpenMesh::PolyMesh_ArrayKernelT& sm) +{ + sm.clear(); + sm.garbage_collection(true, true, true); + CGAL_postcondition(num_edges(sm) == 0); + CGAL_postcondition(num_vertices(sm) == 0); + CGAL_postcondition(num_faces(sm) == 0); +} + +} + #ifndef CGAL_NO_DEPRECATED_CODE #include diff --git a/BGL/include/CGAL/boost/graph/helpers.h b/BGL/include/CGAL/boost/graph/helpers.h index c45912d5484..54db25abcc6 100644 --- a/BGL/include/CGAL/boost/graph/helpers.h +++ b/BGL/include/CGAL/boost/graph/helpers.h @@ -24,7 +24,7 @@ #include #include #include - +#include namespace CGAL { @@ -663,6 +663,60 @@ make_tetrahedron(const P& p0, const P& p1, const P& p2, const P& p3, Graph& g) } +namespace internal { + +template +inline +typename boost::enable_if, void>::type +clear_impl(FaceGraph& g) +{ g.clear(); } + +template +inline +typename boost::disable_if, void>::type +clear_impl(FaceGraph& g) +{ + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + BOOST_FOREACH(edge_descriptor ed, edges(g)) { + remove_edge(ed, g); + } + BOOST_FOREACH(vertex_descriptor vd, vertices(g)) { + remove_vertex(vd, g); + } + BOOST_FOREACH(face_descriptor fd, faces(g)) { + remove_face(fd, g); + } +} + +} + +/** + * \ingroup PkgBGLHelperFct + * + * removes all vertices, faces and halfedges from a graph. Calls + * `remove_edge()`, `remove_vertex()`, and `remove_face()` for each + * edge, vertex or face. + * + * If the graph has a member function `clear`, it will be called + * instead. + * + * @tparam FaceGraph model of `MutableHalfedgeGraph` and `MutableFaceGraph` + * + * @param g the graph to clear + * + **/ +template +void clear(FaceGraph& g) +{ + internal::clear_impl(g); + CGAL_postcondition(num_edges(g) == 0); + CGAL_postcondition(num_vertices(g) == 0); + CGAL_postcondition(num_faces(g) == 0); +} + + } // namespace CGAL #endif // CGAL_BOOST_GRAPH_HELPERS_H diff --git a/BGL/include/CGAL/boost/graph/internal/Has_member_clear.h b/BGL/include/CGAL/boost/graph/internal/Has_member_clear.h new file mode 100644 index 00000000000..4690aacbcbd --- /dev/null +++ b/BGL/include/CGAL/boost/graph/internal/Has_member_clear.h @@ -0,0 +1,26 @@ +#ifndef CGAL_HAS_MEMBER_CLEAR_H +#define CGAL_HAS_MEMBER_CLEAR_H + +namespace CGAL { +namespace internal { + +template +class Has_member_clear +{ +private: + template + class check {}; + + template + static char f(check*); + + template + static int f(...); +public: + static const bool value = (sizeof(f(0)) == sizeof(char)); +}; + +} // internal +} // cgal + +#endif /* CGAL_HAS_MEMBER_CLEAR_H */ diff --git a/BGL/test/BGL/CMakeLists.txt b/BGL/test/BGL/CMakeLists.txt index 19320244b73..999e5f23493 100644 --- a/BGL/test/BGL/CMakeLists.txt +++ b/BGL/test/BGL/CMakeLists.txt @@ -67,16 +67,24 @@ create_single_source_cgal_program( "graph_concept_Polyhedron_3.cpp" ) create_single_source_cgal_program( "graph_concept_Triangulation_2.cpp" ) +create_single_source_cgal_program( "test_clear.cpp" ) + create_single_source_cgal_program( "test_graph_geometry.cpp" ) create_single_source_cgal_program( "test_helpers.cpp" ) +create_single_source_cgal_program( "test_Has_member_clear.cpp" ) + create_single_source_cgal_program( "test_cgal_bgl_named_params.cpp" ) if(OpenMesh_FOUND) target_link_libraries( test_graph_geometry ${OPENMESH_LIBRARIES}) endif() +if(OpenMesh_FOUND) + target_link_libraries( test_clear ${OPENMESH_LIBRARIES}) +endif() + create_single_source_cgal_program( "test_Euler_operations.cpp" ) if(OpenMesh_FOUND) target_link_libraries( test_Euler_operations ${OPENMESH_LIBRARIES}) diff --git a/BGL/test/BGL/test_Has_member_clear.cpp b/BGL/test/BGL/test_Has_member_clear.cpp new file mode 100644 index 00000000000..5382bd8e388 --- /dev/null +++ b/BGL/test/BGL/test_Has_member_clear.cpp @@ -0,0 +1,27 @@ +#include +#include + +struct with_clear { + void clear() {} +}; + +struct wo_clear { }; + +struct with_clear_but_args { + void clear(int) {} +}; + +struct with_clear_but_const { + void clear() const {} +}; + + +int main() +{ + using namespace CGAL::internal; + CGAL_static_assertion(Has_member_clear::value); + CGAL_static_assertion(!Has_member_clear::value); + CGAL_static_assertion(!Has_member_clear::value); + CGAL_static_assertion(!Has_member_clear::value); + return 0; +} diff --git a/BGL/test/BGL/test_clear.cpp b/BGL/test/BGL/test_clear.cpp new file mode 100644 index 00000000000..048d35a0506 --- /dev/null +++ b/BGL/test/BGL/test_clear.cpp @@ -0,0 +1,28 @@ +#include "test_Prefix.h" + +#include + +template +void test() { + const std::string fname = "data/7_faces_triangle.off"; + Mesh m; + if(!read_a_mesh(m, fname)) { + std::cout << "Error reading file: " << fname << std::endl; + } + + CGAL::clear(m); + assert(num_vertices(m) == 0); + assert(num_faces(m) == 0); + assert(num_edges(m) == 0); + assert(is_valid(m)); +} + +int main() +{ + test(); + test(); +#if defined(CGAL_USE_OPENMESH) + test(); +#endif + return 0; +} diff --git a/Installation/changes.html b/Installation/changes.html index f8336c80525..289b199d2d8 100644 --- a/Installation/changes.html +++ b/Installation/changes.html @@ -289,6 +289,10 @@ and src/ directories). CGAL::expand_vertex_selection(), CGAL::reduce_vertex_selection() and CGAL::select_incident_faces(). +
  • + Add a helper function CGAL::clear which clears a + MutableFaceGraph efficiently and generically. +
  • diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h index 905f2ed5797..3a06cd1420d 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h @@ -607,6 +607,14 @@ std::size_t keep_largest_connected_components(PolygonMesh& pmesh boost::vector_property_map face_cc(fim); std::size_t num = connected_components(pmesh, face_cc, np); + // Even even we do not want to keep anything we need to first + // calculate the number of existing connected_components to get the + // correct return value. + if(nb_components_to_keep == 0) { + CGAL::clear(pmesh); + return num; + } + if((num == 1)|| (nb_components_to_keep > num) ) return 0; diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/connected_component_polyhedron.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/connected_component_polyhedron.cpp index 2b2e618de45..b9d1fcb9f7e 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/connected_component_polyhedron.cpp +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/connected_component_polyhedron.cpp @@ -53,9 +53,9 @@ void mesh_with_id(const char* argv1) PMP::keep_largest_connected_components(sm,2); - std::ofstream ofile("blobby_2cc_id.off"); - ofile << sm << std::endl; - ofile.close(); + std::ofstream ofile("blobby_2cc_id.off"); + ofile << sm << std::endl; + ofile.close(); } void mesh_no_id(const char* argv1) @@ -150,11 +150,38 @@ void test_border_cases() assert(num_vertices(copy)==0); } +void keep_nothing(const char* argv1) +{ + typedef boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef boost::graph_traits::face_descriptor face_descriptor; + + Mesh_with_id sm; + std::ifstream in(argv1); + if(!(in >> sm)) { + std::cerr << "ERROR reading file: " << argv1 << std::endl; + return; + } + int i=0; + BOOST_FOREACH(face_descriptor f, faces(sm)){ + f->id() = i++; + } + i=0; + BOOST_FOREACH(vertex_descriptor v, vertices(sm)){ + v->id() = i++; + } + + PMP::keep_largest_connected_components(sm, 0); + assert(num_vertices(sm) == 0); + assert(num_edges(sm) == 0); + assert(num_faces(sm) == 0); +} + int main(int argc, char* argv[]) { const char* filename = (argc > 1) ? argv[1] : "data/blobby_3cc.off"; mesh_with_id(filename); mesh_no_id(filename); test_border_cases(); + keep_nothing(filename); return 0; }