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` 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/BGL/include/CGAL/boost/graph/parameters_interface.h b/BGL/include/CGAL/boost/graph/parameters_interface.h index 1ec0f1504de..01c9796e813 100644 --- a/BGL/include/CGAL/boost/graph/parameters_interface.h +++ b/BGL/include/CGAL/boost/graph/parameters_interface.h @@ -78,6 +78,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 019ad98dedf..22252abdd36 100644 --- a/BGL/test/BGL/test_cgal_bgl_named_params.cpp +++ b/BGL/test/BGL/test_cgal_bgl_named_params.cpp @@ -101,6 +101,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 @@ -177,6 +178,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_type<47>(get_param(np, CGAL::internal_np::output_iterator)); } int main() @@ -238,6 +240,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/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` diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt index 839bd7b52ff..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,6 +364,12 @@ should be considered as part of the clipping volume or not. \b Default value is `true` \cgalNPEnd +\cgalNPBegin{output_iterator} \anchor PMP_output_iterator +Parameter to pass an output iterator. +\n +\b Type : a model of `OutputIterator` \n +\b Default : `Emptyset_iterator` +\cgalNPEnd \cgalNPTableEnd 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/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index bfbe181b40e..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,6 +30,7 @@ #include #include #include +#include #include #include @@ -90,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 { @@ -325,6 +311,7 @@ namespace internal { public: Incremental_remesher(PolygonMesh& pmesh , VertexPointMap& vpmap + , const GeomTraits& gt , const bool protect_constraints , EdgeIsConstrainedMap ecmap , VertexIsConstrainedMap vcmap @@ -333,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_() @@ -364,9 +352,10 @@ 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_, 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); @@ -817,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 } @@ -922,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 @@ -964,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; @@ -1458,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 @@ -1513,7 +1491,7 @@ private: // update status using constrained edge map if (!boost::is_same >::value) + Constant_property_map >::value) { BOOST_FOREACH(edge_descriptor e, edges(mesh_)) { @@ -1587,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(h, mesh_, vpmap_, GeomTraits())) + 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(h, mesh_, vpmap_, GeomTraits())) + 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; @@ -1653,11 +1635,15 @@ private: short_edges.insert(typename Bimap::value_type(hf, sqlen)); } - if (!is_border(hf, mesh_) - && is_degenerate_triangle_face(hf, mesh_, vpmap_, GeomTraits())) + 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(hfo, mesh_, vpmap_, GeomTraits())) + 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; @@ -1674,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(h, mesh_, vpmap_, GeomTraits())) + 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; @@ -1817,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; } @@ -1829,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; } @@ -1916,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/merge_border_vertices.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h new file mode 100644 index 00000000000..6f0185533ef --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/merge_border_vertices.h @@ -0,0 +1,290 @@ +// 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_POLYGON_MESH_PROCESSING_MERGE_BORDER_VERTICES_H +#define CGAL_POLYGON_MESH_PROCESSING_MERGE_BORDER_VERTICES_H + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace CGAL { + +namespace Polygon_mesh_processing { + +namespace internal { + +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()(const std::pair& h1, + const std::pair& h2) const + { + 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; + const VertexPointMap& vpm; +}; + + +// warning: cycle_hedges will be altered (sorted) +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) +{ + // 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; + + std::set< std::pair > intervals; + + while(i!=nbv) + { + 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].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].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; + break; + } + } + } + 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 + +/// \ingroup PMP_repairing_grp +/// 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. +/// +template +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); + + std::vector vertices_to_rm; + + BOOST_FOREACH(halfedge_descriptor in_h_rm, sorted_hedges) + { + 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 h, halfedges_around_target(v_kept, pm)) + { + if (edge(vd, source(h, pm), pm).second) + { + shall_continue=true; + break; + } + } + if (shall_continue) continue; // skip vertices already incident to the same vertex + // 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) + remove_vertex(vd, pm); +} + +/// \ingroup PMP_repairing_grp +/// 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, + PolygonMesh& pm, + const NamedParameter& np) +{ + typedef typename boost::graph_traits::halfedge_descriptor halfedge_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 halfedges of the cycle + std::vector< std::pair > cycle_hedges; + halfedge_descriptor start=h; + std::size_t index=0; + do{ + 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_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(); + // hedges are sorted along the cycle + merge_vertices_in_range(hedges, pm); + } +} + +/// \ingroup PMP_repairing_grp +/// 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) +{ + 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()); +} + +template +void merge_duplicated_vertices_in_boundary_cycle( + typename boost::graph_traits::halfedge_descriptor h, + PolygonMesh& pm) +{ + merge_duplicated_vertices_in_boundary_cycle(h, 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/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..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), @@ -175,18 +176,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 < @@ -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)); @@ -351,22 +354,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, gt, 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 6fcb1e929dd..9f9a1d7a4e2 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,9 @@ #include - -#include -#include -#include #include #include +#include #include #include @@ -40,21 +37,33 @@ #include #include +#include #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, @@ -142,8 +151,11 @@ namespace debug{ << vids[ target(next(next(halfedge(f, tm), tm), tm), tm) ] << "\n"; } } + } //end of namespace debug +namespace internal { + template struct Less_vertex_point{ typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; @@ -157,33 +169,139 @@ struct Less_vertex_point{ } }; -///\cond SKIP_IN_MANUAL +} // end namespace internal -template -OutputIterator -degenerate_faces(const TriangleMesh& tm, - const VertexPointMap& vpmap, - const Traits& traits, - OutputIterator out) +/// \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::face_descriptor face_descriptor; - BOOST_FOREACH(face_descriptor fd, faces(tm)) + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + + BOOST_FOREACH(edge_descriptor ed, edges) { - if ( is_degenerate_triangle_face(fd, tm, vpmap, traits) ) - *out++=fd; + 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) +degenerate_edges(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_edges(edges(tm), tm, out, CGAL::parameters::all_default()); +} - return degenerate_faces(tm, get(vertex_point, tm), Kernel(), out); +/// \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) + { + if(is_degenerate_triangle_face(fd, tm, np)) + *out++ = fd; + } + return 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(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. @@ -365,10 +483,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 +502,24 @@ 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; - 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); - } + std::set degenerate_edges_to_remove; + degenerate_edges(edge_range, tmesh, std::inserter(degenerate_edges_to_remove, + degenerate_edges_to_remove.end())); - #ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG - std::cout << "Found " << null_edges_to_remove.size() << " null edges.\n"; - #endif +#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG + 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 +529,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 +549,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 +735,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 +755,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 +766,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 @@ -674,15 +786,15 @@ std::size_t remove_null_edges( /// @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 @@ -692,6 +804,7 @@ std::size_t remove_null_edges( /// /// \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, @@ -717,8 +830,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 { @@ -731,9 +845,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, vpmap, traits) ) - 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 @@ -763,7 +876,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, np)) { degenerate_face_set.insert( face(hd, tmesh) ); } @@ -964,7 +1077,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 = @@ -1281,7 +1394,6 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh, return nb_deg_faces; } - template std::size_t remove_degenerate_faces(TriangleMesh& tmesh) { @@ -1289,52 +1401,194 @@ 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) +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 +/// 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 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" +/// +/// @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 `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} a model of `OutputIterator` with value type +/// `std::vector`. The first vertex of each vector is a non-manifold vertex +/// of the input mesh, followed by the new vertices that were created to fix this precise +/// non-manifold configuration. +/// \cgalParamEnd +/// \cgalNamedParamsEnd +/// +/// \return the number of vertices created. +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 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, + Constant_property_map // default (no constraint pmap) + > ::type VerticesMap; + VerticesMap cmap + = choose_param(get_param(np, internal_np::vertex_is_constrained), + Constant_property_map(false)); + + 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; - 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; 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{ + 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; @@ -1343,12 +1597,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. /// A vertex is considered isolated if it is not incident to any simplex diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/shape_predicates.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/shape_predicates.h new file mode 100644 index 00000000000..dc66aecacf9 --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/shape_predicates.h @@ -0,0 +1,334 @@ +// Copyright (c) 2015, 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) : Konstantinos Katrioplas, +// Mael Rouxel-Labbé + +#ifndef CGAL_POLYGON_MESH_PROCESSING_SHAPE_PREDICATES_H +#define CGAL_POLYGON_MESH_PROCESSING_SHAPE_PREDICATES_H + +#include +#include + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +namespace CGAL { + +namespace Polygon_mesh_processing { + +/// \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. +/// +/// @tparam PolygonMesh a model of `HalfedgeGraph` +/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters" +/// +/// @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 `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 two points are identical. +/// \cgalParamEnd +/// \cgalNamedParamsEnd +/// +/// \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, + 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()); + + return traits.equal_3_object()(get(vpmap, source(e, pm)), get(vpmap, target(e, pm))); +} + +template +bool is_degenerate_edge(typename boost::graph_traits::edge_descriptor e, + const PolygonMesh& pm) +{ + 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 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 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 `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 +/// +/// \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_precondition(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 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 +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 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 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 `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 `FT` and +/// the nested functor `Compute_squared_distance_3`. +/// \cgalParamEnd +/// \cgalNamedParamsEnd +/// +/// \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, + const TriangleMesh& tm, + const double threshold, + const NamedParameters& np) +{ + 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; + 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)) + { + const FT sq_length = traits.compute_squared_distance_3_object()(get(vpmap, source(h, tm)), + get(vpmap, target(h, tm))); + + 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; + } + } + + 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 +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 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 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 `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 functors `Compute_squared_distance_3`, `Construct_vector_3`, +/// and `Compute_scalar_product_3`. +/// \cgalParamEnd +/// \cgalNamedParamsEnd +/// +/// \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, + const TriangleMesh& tm, + const double threshold, + const NamedParameters& np) +{ + 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; + 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)) + { + sq_lengths[pos++] = traits.compute_squared_distance_3_object()(get(vpmap, source(h, tm)), + get(vpmap, target(h, tm))); + } + + 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 +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_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 6ffde721a7d..2becf2af309 100644 --- a/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/CMakeLists.txt @@ -101,6 +101,9 @@ endif() create_single_source_cgal_program("test_orient_cc.cpp") create_single_source_cgal_program("test_pmp_transform.cpp") create_single_source_cgal_program("extrude_test.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_shape_predicates.cpp") if( TBB_FOUND ) CGAL_target_use_TBB(test_pmp_distance) 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..7a83712189f --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/data/merge_points.off @@ -0,0 +1,95 @@ +OFF +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/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/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/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_degeneracies_test.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/remove_degeneracies_test.cpp index 386e27723a1..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,20 +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; } 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 new file mode 100644 index 00000000000..ed21427f4d1 --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_merging_border_vertices.cpp @@ -0,0 +1,54 @@ +#include +#include + +#include +#include + +#include + + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Surface_mesh; + + +void test_merge_duplicated_vertices_in_boundary_cycles(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 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; + } +} + +int main(int argc, char** argv) +{ + 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 EXIT_SUCCESS; +} diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_shape_predicates.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_shape_predicates.cpp new file mode 100644 index 00000000000..7526ac80bec --- /dev/null +++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_shape_predicates.cpp @@ -0,0 +1,248 @@ +#include + +#include + +#include +#include + +#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::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"; + std::exit(1); + } + std::vector all_edges(edges(mesh).begin(), edges(mesh).end()); + + 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::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"; + std::exit(1); + } + + std::vector all_faces(faces(mesh).begin(), faces(mesh).end()); + 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 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) +{ + 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); +} + +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"; + std::exit(1); + } + + size_type ini_nv = num_vertices(mesh); + + // create non-manifold vertex + 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(); + + assert(num_vertices(mesh) == ini_nv - 1); + + BOOST_FOREACH(vertex_descriptor v, vertices(mesh)) + { + if(v == vertex_to_merge_onto) + assert(CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh)); + else + assert(!CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh)); + } + + std::cout << "done" << std::endl; +} + +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"; + std::exit(1); + } + const std::size_t initial_vertices = num_vertices(mesh); + + // 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_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"; + std::exit(1); + } + + 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_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 EXIT_SUCCESS; +} diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Degenerated_faces_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Degenerated_faces_plugin.cpp index 0b0d62482a1..929890a4d8b 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Degenerated_faces_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Degenerated_faces_plugin.cpp @@ -19,6 +19,9 @@ #include #include #include + +#include + #ifdef USE_SURFACE_MESH typedef Scene_surface_mesh_item Scene_facegraph_item; #else @@ -87,7 +90,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 51290afe60e..0783bee136b 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 @@ -741,14 +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::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..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,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 c9c5784304b..2acb48a2bdd 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 @@ -285,7 +286,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()) { @@ -1675,7 +1676,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 f1241d3644a..95fde349ba4 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 @@ -2076,8 +2077,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::is_degenerate_triangle_face(res, *item->polyhedron(), get(CGAL::vertex_point, *item->polyhedron()), Kernel())) + 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 7d248692a88..aaa47d034c1 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)) @@ -1501,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 28799d40fd9..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,11 +8,15 @@ #include #include #include -#include #include #include +#include +#include +#include +#include +#include template void angles(Mesh* poly, double& mini, double& maxi, double& ave) @@ -83,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::is_degenerate_triangle_face(f, *poly, vpmap, Traits())) - ++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 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) { }