Merge pull request #293 from sloriot/PMP-fix_remove_degenerate_faces-sloriot

Polygon mesh processing:  fix remove_degenerate_faces() but do not publish it in the documentation
This commit is contained in:
Laurent Rineau 2015-09-04 11:24:36 +02:00
commit 59ac9f30fc
8 changed files with 359 additions and 412 deletions

View File

@ -68,8 +68,9 @@ and provides a list of the parameters that are used in this package.
- \link stitching_grp `CGAL::Polygon_mesh_processing::stitch_borders()` \endlink - \link stitching_grp `CGAL::Polygon_mesh_processing::stitch_borders()` \endlink
- `CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh()` - `CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh()`
- `CGAL::Polygon_mesh_processing::orient_polygon_soup()` - `CGAL::Polygon_mesh_processing::orient_polygon_soup()`
<!---
- \link reverse_face_orientations_grp `CGAL::Polygon_mesh_processing::remove_degenerate_faces()` \endlink - \link reverse_face_orientations_grp `CGAL::Polygon_mesh_processing::remove_degenerate_faces()` \endlink
--->
## Normal Computation Functions ## ## Normal Computation Functions ##
- `CGAL::Polygon_mesh_processing::compute_face_normal()` - `CGAL::Polygon_mesh_processing::compute_face_normal()`
- `CGAL::Polygon_mesh_processing::compute_face_normals()` - `CGAL::Polygon_mesh_processing::compute_face_normals()`
@ -93,4 +94,5 @@ It can be made short to PM. And TriangleMesh (or TM) specifies when the paramete
\todo document `BGL/include/CGAL/boost/graph/split_graph_into_polylines.h` \todo document `BGL/include/CGAL/boost/graph/split_graph_into_polylines.h`
\todo add in BGL `clear(pmesh)` and use it in `keep_largest_connected_components(pmesh, 0);` \todo add in BGL `clear(pmesh)` and use it in `keep_largest_connected_components(pmesh, 0);`
\todo document BGL/include/CGAL/boost/graph/Dual.h and remove the example from dont_submit \todo document BGL/include/CGAL/boost/graph/Dual.h and remove the example from dont_submit
\todo fix and restore remove_degenerate_faces in the user and the reference manual
*/ */

View File

@ -260,6 +260,7 @@ with duplicated border edges.
\cgalExample{Polygon_mesh_processing/stitch_borders_example.cpp} \cgalExample{Polygon_mesh_processing/stitch_borders_example.cpp}
******************* *******************
<!---
\subsection DegenerateFaces Removing Degenerate Faces \subsection DegenerateFaces Removing Degenerate Faces
Some degenerate faces may be part of a given triangle mesh. Some degenerate faces may be part of a given triangle mesh.
@ -277,7 +278,7 @@ are removed, the connectivity is fixed, and the number of removed faces
is output. is output.
\cgalExample{Polygon_mesh_processing/remove_degeneracies_example.cpp} \cgalExample{Polygon_mesh_processing/remove_degeneracies_example.cpp}
--->
******************* *******************
\subsection PolygonSoups Polygon Soups \subsection PolygonSoups Polygon Soups
@ -445,7 +446,6 @@ Function or Class | Pure Triangle | Self-Intersection Free
`stitch_borders()` | no | no | no `stitch_borders()` | no | no | no
`polygon_soup_to_polygon_mesh()` | no | no | no `polygon_soup_to_polygon_mesh()` | no | no | no
`orient_polygon_soup()` | no | no | no `orient_polygon_soup()` | no | no | no
`remove_degenerate_faces()` | yes | no | no
`compute_face_normal()` | no | no | no `compute_face_normal()` | no | no | no
`compute_face_normals()` | no | no | no `compute_face_normals()` | no | no | no
`compute_vertex_normal()` | no | no | no `compute_vertex_normal()` | no | no | no
@ -458,6 +458,9 @@ Function or Class | Pure Triangle | Self-Intersection Free
`keep_connected_components()` | no | no | no `keep_connected_components()` | no | no | no
`remove_connected_components()` | no | no | no `remove_connected_components()` | no | no | no
</center> </center>
<!---
`remove_degenerate_faces()` | yes | no | no
--->
**************************************** ****************************************
\section PMPHistory Implementation History \section PMPHistory Implementation History

View File

@ -11,6 +11,5 @@
\example Polygon_mesh_processing/triangulate_polyline_example.cpp \example Polygon_mesh_processing/triangulate_polyline_example.cpp
\example Polygon_mesh_processing/refine_fair_example.cpp \example Polygon_mesh_processing/refine_fair_example.cpp
\example Polygon_mesh_processing/mesh_slicer_example.cpp \example Polygon_mesh_processing/mesh_slicer_example.cpp
\example Polygon_mesh_processing/remove_degeneracies_example.cpp
*/ */

View File

@ -0,0 +1,2 @@
examples/Polygon_mesh_processing/remove_degeneracies_example.cpp
examples/Polygon_mesh_processing/remove_degeneracies_example_OM.cpp

View File

@ -81,7 +81,7 @@ create_single_source_cgal_program( "connected_components_example.cpp")
create_single_source_cgal_program( "polygon_soup_example.cpp") create_single_source_cgal_program( "polygon_soup_example.cpp")
create_single_source_cgal_program( "triangulate_polyline_example.cpp") create_single_source_cgal_program( "triangulate_polyline_example.cpp")
create_single_source_cgal_program( "mesh_slicer_example.cpp") create_single_source_cgal_program( "mesh_slicer_example.cpp")
create_single_source_cgal_program( "remove_degeneracies_example.cpp") #create_single_source_cgal_program( "remove_degeneracies_example.cpp")
if(NOT (${EIGEN3_VERSION} VERSION_LESS 3.2.0)) if(NOT (${EIGEN3_VERSION} VERSION_LESS 3.2.0))
create_single_source_cgal_program( "hole_filling_example.cpp" ) create_single_source_cgal_program( "hole_filling_example.cpp" )
@ -104,8 +104,8 @@ target_link_libraries( point_inside_example_OM ${OPENMESH_LIBRARIES} )
create_single_source_cgal_program( "stitch_borders_example_OM.cpp" ) create_single_source_cgal_program( "stitch_borders_example_OM.cpp" )
target_link_libraries( stitch_borders_example_OM ${OPENMESH_LIBRARIES} ) target_link_libraries( stitch_borders_example_OM ${OPENMESH_LIBRARIES} )
create_single_source_cgal_program( "remove_degeneracies_example_OM.cpp") #create_single_source_cgal_program( "remove_degeneracies_example_OM.cpp")
target_link_libraries( remove_degeneracies_example_OM ${OPENMESH_LIBRARIES} ) #target_link_libraries( remove_degeneracies_example_OM ${OPENMESH_LIBRARIES} )
create_single_source_cgal_program( "triangulate_faces_example_OM.cpp") create_single_source_cgal_program( "triangulate_faces_example_OM.cpp")
target_link_libraries( triangulate_faces_example_OM ${OPENMESH_LIBRARIES} ) target_link_libraries( triangulate_faces_example_OM ${OPENMESH_LIBRARIES} )

View File

@ -33,6 +33,61 @@
namespace CGAL{ namespace CGAL{
namespace Polygon_mesh_processing { namespace Polygon_mesh_processing {
namespace debug{
template <class TriangleMesh, class VertexPointMap>
std::ostream& dump_edge_neighborhood(
typename boost::graph_traits<TriangleMesh>::edge_descriptor ed,
TriangleMesh& tmesh,
const VertexPointMap& vpmap,
std::ostream& out)
{
typedef boost::graph_traits<TriangleMesh> GT;
typedef typename GT::halfedge_descriptor halfedge_descriptor;
typedef typename GT::vertex_descriptor vertex_descriptor;
typedef typename GT::face_descriptor face_descriptor;
halfedge_descriptor h = halfedge(ed, tmesh);
std::map<vertex_descriptor, int> vertices;
std::set<face_descriptor> faces;
int vindex=0;
BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(h, tmesh))
{
if ( vertices.insert(std::make_pair(source(hd, tmesh), vindex)).second )
++vindex;
if (!is_border(hd, tmesh))
faces.insert( face(hd, tmesh) );
}
h=opposite(h, tmesh);
BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(h, tmesh))
{
if ( vertices.insert(std::make_pair(source(hd, tmesh), vindex)).second )
++vindex;
if (!is_border(hd, tmesh))
faces.insert( face(hd, tmesh) );
}
std::vector<vertex_descriptor> ordered_vertices(vertices.size());
typedef std::pair<const vertex_descriptor, int> Pair_type;
BOOST_FOREACH(const Pair_type& p, vertices)
ordered_vertices[p.second]=p.first;
out << "OFF\n" << ordered_vertices.size() << " " << faces.size() << " 0\n";
BOOST_FOREACH(vertex_descriptor vd, ordered_vertices)
out << get(vpmap, vd) << "\n";
BOOST_FOREACH(face_descriptor fd, faces)
{
out << "3";
h=halfedge(fd,tmesh);
BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_face(h, tmesh))
out << " " << vertices[target(hd, tmesh)];
out << "\n";
}
return out;
}
} //end of namespace debug
template <class HalfedgeGraph, class VertexPointMap, class Traits> template <class HalfedgeGraph, class VertexPointMap, class Traits>
struct Less_vertex_point{ struct Less_vertex_point{
typedef typename boost::graph_traits<HalfedgeGraph>::vertex_descriptor vertex_descriptor; typedef typename boost::graph_traits<HalfedgeGraph>::vertex_descriptor vertex_descriptor;
@ -85,188 +140,11 @@ bool is_degenerated(
return is_degenerated(halfedge(fd,tmesh), tmesh, vpmap, traits); return is_degenerated(halfedge(fd,tmesh), tmesh, vpmap, traits);
} }
#if 0 ///\cond SKIP_IN_MANUAL
namespace internal { template <class EdgeRange, class TriangleMesh, class NamedParameters>
std::size_t remove_null_edges(
// hbase is an edge of length 0 we cannot contract because const EdgeRange& edge_range,
// the link condition is not satisfied.
// In this function we look whether the condition is not
// satisfied because of some faces on the "side" of hbase.
// Here we look for two halfedges that together with hbase
// enclose a set of degenerate faces so as to replace that
// set with only one triangle
template <class Traits, class TriangleMesh, class VertexPointMap>
boost::optional<
std::pair<
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor,
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor >
>
find_larger_triangle(
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor hbase,
TriangleMesh& tmesh, TriangleMesh& tmesh,
const VertexPointMap& vpmap,
const Traits& traits)
{
typedef typename boost::graph_traits<TriangleMesh> GT;
typedef typename GT::halfedge_descriptor halfedge_descriptor;
bool no_pb=false;
// hbase = v0 -> v1
// consider hedges with v0 as target
halfedge_descriptor left_hedge = prev(hbase, tmesh), o_right_hedge=left_hedge;
do
{
left_hedge = prev( opposite(left_hedge, tmesh), tmesh );
if ( face(left_hedge, tmesh) == GT::null_face() ||
!is_degenerated(left_hedge, tmesh, vpmap, traits) )
{
no_pb=true;
break;
}
// look for a halfedge with v1 as target
BOOST_FOREACH(o_right_hedge, halfedges_around_source(left_hedge, tmesh) )
{
if ( target(o_right_hedge, tmesh) == target(hbase, tmesh) )
{
if ( !is_degenerated( opposite(o_right_hedge, tmesh), tmesh, vpmap, traits) )
no_pb=true;
break;
}
}
if ( target(o_right_hedge, tmesh) == target(hbase, tmesh) )
break;
}
while(true);
if (!no_pb)
return std::make_pair(left_hedge, opposite(o_right_hedge, tmesh));
return boost::none;
}
// h2 is a halfedge of length 0 but collapsing the edge
// is not directly possible because the link condition is not
// satisfied. We remove all simplices bounded by h1, h2 and h3
// so as to keep only that triangle
template <class TriangleMesh, class EdgeMap>
void
remove_faces_inside_triangle(
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor h1,
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor h2,
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor h3,
TriangleMesh& tmesh,
EdgeMap& edge_map1,
EdgeMap& edge_map2)
{
typedef typename boost::graph_traits<TriangleMesh> GT;
typedef typename GT::edge_descriptor edge_descriptor;
typedef typename GT::halfedge_descriptor halfedge_descriptor;
typedef typename GT::face_descriptor face_descriptor;
typedef typename GT::vertex_descriptor vertex_descriptor;
CGAL_assertion( source(h1,tmesh) == target(h3, tmesh) );
CGAL_assertion( source(h2,tmesh) == target(h1, tmesh) );
CGAL_assertion( source(h3,tmesh) == target(h2, tmesh) );
std::set<face_descriptor> all_faces;
all_faces.insert(face(h1, tmesh));
all_faces.insert(face(h2, tmesh));
all_faces.insert(face(h3, tmesh));
std::vector<halfedge_descriptor> queue;
queue.push_back( opposite(prev(h1,tmesh), tmesh) );
queue.push_back( opposite(next(h1,tmesh), tmesh) );
queue.push_back( opposite(prev(h2,tmesh), tmesh) );
queue.push_back( opposite(next(h2,tmesh), tmesh) );
queue.push_back( opposite(prev(h3,tmesh), tmesh) );
queue.push_back( opposite(next(h3,tmesh), tmesh) );
std::set<edge_descriptor> all_edges;
while(!queue.empty())
{
halfedge_descriptor back=queue.back();
queue.pop_back();
all_edges.insert( edge(back, tmesh) );
if ( all_faces.insert( face(back, tmesh) ).second )
{
queue.push_back( opposite(prev(back,tmesh), tmesh) );
queue.push_back( opposite(next(back,tmesh), tmesh) );
}
}
std::set<vertex_descriptor> all_vertices;
BOOST_FOREACH(edge_descriptor ed, all_edges)
{
all_vertices.insert( source(ed, tmesh) );
all_vertices.insert( target(ed, tmesh) );
}
all_vertices.erase( target(h1, tmesh) );
all_vertices.erase( target(h2, tmesh) );
all_vertices.erase( target(h3, tmesh) );
// create the triangle
face_descriptor remaining_face = *all_faces.begin();
all_faces.erase(all_faces.begin());
// update next-prev pointers
set_next(h1, h2, tmesh);
set_next(h2, h3, tmesh);
set_next(h3, h1, tmesh);
// update vertex pointers
set_halfedge(target(h1,tmesh), h1, tmesh);
set_halfedge(target(h2,tmesh), h2, tmesh);
set_halfedge(target(h3,tmesh), h3, tmesh);
// update face-halfedge pointers
set_face(h1, remaining_face, tmesh);
set_face(h2, remaining_face, tmesh);
set_face(h3, remaining_face, tmesh);
set_halfedge(remaining_face, h2, tmesh);
// remove interior simplices
BOOST_FOREACH(edge_descriptor ed, all_edges){
edge_map1.erase(ed);
edge_map2.erase(ed);
remove_edge(ed, tmesh);
}
BOOST_FOREACH(vertex_descriptor vd, all_vertices)
remove_vertex(vd, tmesh);
BOOST_FOREACH(face_descriptor fd, all_faces)
remove_face(fd, tmesh);
}
} // end of namespace internal
#endif
/// \ingroup PkgPolygonMeshProcessing
/// removes the degenerate faces from a triangulated surface mesh.
/// A face is considered degenerate if two of its vertices share the same location,
/// or more generally if all its vertices are collinear.
///
/// @pre `CGAL::is_triangle_mesh(tmesh)`
///
/// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph`
/// that has an internal property map for `boost::vertex_point_t`
/// @tparam NamedParameters a sequence of \ref namedparameters
///
/// @param tmesh the triangulated surface mesh to be repaired
/// @param np optional \ref namedparameters 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` \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
/// - for each functor Foo, a function `Foo foo_object()`
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
/// \return number of removed degenerate faces
///
template <class TriangleMesh, class NamedParameters>
std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
const NamedParameters& np) const NamedParameters& np)
{ {
CGAL_assertion(CGAL::is_triangle_mesh(tmesh)); CGAL_assertion(CGAL::is_triangle_mesh(tmesh));
@ -291,18 +169,14 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
std::size_t nb_deg_faces = 0; std::size_t nb_deg_faces = 0;
// First remove edges of length 0 // collect edges of length 0
std::set<edge_descriptor> null_edges_to_remove; std::set<edge_descriptor> null_edges_to_remove;
BOOST_FOREACH(edge_descriptor ed, edge_range)
BOOST_FOREACH(edge_descriptor ed, edges(tmesh))
{ {
if ( traits.equal_3_object()(get(vpmap, target(ed, tmesh)), get(vpmap, source(ed, tmesh))) ) if ( traits.equal_3_object()(get(vpmap, target(ed, tmesh)), get(vpmap, source(ed, tmesh))) )
null_edges_to_remove.insert(ed); null_edges_to_remove.insert(ed);
} }
std::size_t nb_edges_previously_skipped = 0;
do{
std::set<edge_descriptor> edges_to_remove_skipped;
while (!null_edges_to_remove.empty()) while (!null_edges_to_remove.empty())
{ {
edge_descriptor ed = *null_edges_to_remove.begin(); edge_descriptor ed = *null_edges_to_remove.begin();
@ -317,51 +191,70 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
{ {
++nb_deg_faces; ++nb_deg_faces;
null_edges_to_remove.erase(edge(prev(h, tmesh), tmesh)); null_edges_to_remove.erase(edge(prev(h, tmesh), tmesh));
edges_to_remove_skipped.erase(edge(prev(h, tmesh), tmesh));
} }
if (face(opposite(h, tmesh), tmesh)!=GT::null_face()) if (face(opposite(h, tmesh), tmesh)!=GT::null_face())
{ {
++nb_deg_faces; ++nb_deg_faces;
null_edges_to_remove.erase(edge(prev(opposite(h, tmesh), tmesh), tmesh)); null_edges_to_remove.erase(edge(prev(opposite(h, tmesh), tmesh), tmesh));
edges_to_remove_skipped.erase(edge(prev(opposite(h, tmesh), tmesh), tmesh));
} }
//now remove the edge //now remove the edge
CGAL::Euler::collapse_edge(ed, tmesh); CGAL::Euler::collapse_edge(ed, tmesh);
} }
else{ else{
//handle the case when the edge is incident to a triangle hole
//we first fill the hole and try again
if ( is_border(ed, tmesh) )
{
halfedge_descriptor hd = halfedge(ed,tmesh);
if (!is_border(hd,tmesh)) hd=opposite(hd,tmesh);
if (is_triangle(hd, tmesh))
{
Euler::fill_hole(hd, tmesh);
null_edges_to_remove.insert(ed);
continue;
}
}
// When the edge does not satisfy the link condition, it means that it cannot be
// collapsed as is. In the following we assume that there is no topological issue
// with contracting the edge (no volume will disappear).
// We start by marking the faces that are incident to an edge endpoint.
// If the set of marked faces is a topologically disk, then we simply remove all the simplicies
// inside the disk and star the hole with the edge vertex kept.
// If the set of marked faces is not a topological disk, it has some non-manifold vertices
// on its boundary. We need to mark additional faces to make it a topological disk.
// We can then apply the star hole procedure.
// Right now we additionally mark the smallest connected components of non-marked faces
// (using the numnber of faces)
//backup central point //backup central point
typename Traits::Point_3 pt = get(vpmap, source(ed, tmesh)); typename Traits::Point_3 pt = get(vpmap, source(ed, tmesh));
// mark faces of the link of each endpoints of the edge which collapse is not topologically valid // mark faces of the link of each endpoints of the edge which collapse is not topologically valid
std::set<face_descriptor> marked_faces; std::set<face_descriptor> marked_faces;
std::vector<halfedge_descriptor> candidate_border;
// first endpoint // first endpoint
BOOST_FOREACH( halfedge_descriptor hd, CGAL::halfedges_around_target(halfedge(ed,tmesh), tmesh) ) BOOST_FOREACH( halfedge_descriptor hd, CGAL::halfedges_around_target(halfedge(ed,tmesh), tmesh) )
{ if (!is_border(hd,tmesh)) marked_faces.insert( face(hd, tmesh) );
marked_faces.insert( face(hd, tmesh) );
candidate_border.push_back( prev(hd, tmesh) );
}
// second endpoint // second endpoint
BOOST_FOREACH( halfedge_descriptor hd, CGAL::halfedges_around_target(opposite(halfedge(ed, tmesh), tmesh), tmesh) ) BOOST_FOREACH( halfedge_descriptor hd, CGAL::halfedges_around_target(opposite(halfedge(ed, tmesh), tmesh), tmesh) )
{ if (!is_border(hd,tmesh)) marked_faces.insert( face(hd, tmesh) );
marked_faces.insert( face(hd, tmesh) );
candidate_border.push_back( prev(hd, tmesh) );
}
// extract the halfedge on the boundary of the marked region // extract the halfedges on the boundary of the marked region
std::vector<halfedge_descriptor> border; std::vector<halfedge_descriptor> border;
BOOST_FOREACH(halfedge_descriptor hd, candidate_border) BOOST_FOREACH(face_descriptor fd, marked_faces)
BOOST_FOREACH(halfedge_descriptor hd, CGAL::halfedges_around_face(halfedge(fd,tmesh), tmesh))
{ {
if ( marked_faces.count( face(hd, tmesh) )!= halfedge_descriptor hd_opp = opposite(hd, tmesh);
marked_faces.count( face(opposite(hd, tmesh), tmesh) ) ) if ( is_border(hd_opp, tmesh) ||
marked_faces.count( face(hd, tmesh) )!=
marked_faces.count( face(hd_opp, tmesh) ) )
{ {
border.push_back( hd ); border.push_back( hd );
} }
} }
// define cc of border halfedges: two halfedges are in the same cc // define cc of border halfedges: two halfedges are in the same cc
// if they are on the border of a set of non-marked faces sharing // if they are on the border of the cc of non-marked faces.
// a common vertex
typedef CGAL::Union_find<halfedge_descriptor> UF_ds; typedef CGAL::Union_find<halfedge_descriptor> UF_ds;
UF_ds uf; UF_ds uf;
std::map<halfedge_descriptor, typename UF_ds::handle> handles; std::map<halfedge_descriptor, typename UF_ds::handle> handles;
@ -390,28 +283,32 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
// The smallest components need to be marked so that the patch // The smallest components need to be marked so that the patch
// made of marked faces is a topological disk // made of marked faces is a topological disk
// extract one border halfedge per cc
std::set< halfedge_descriptor > ccs;
BOOST_FOREACH(halfedge_descriptor hd, border)
ccs.insert( *uf.find( handles[hd] ) );
// we will explore in parallel the connected components and will stop // we will explore in parallel the connected components and will stop
// when all but one connected component have been entirely explored. // when all but one connected component have been entirely explored.
// We have one face at a time for each cc in order to not explore a // We add one face at a time for each cc in order to not explore a
// potentially very large cc. // potentially very large cc.
std::vector< std::vector<halfedge_descriptor> > stacks_per_cc(nb_cc); std::vector< std::vector<halfedge_descriptor> > stacks_per_cc(nb_cc);
std::vector< std::set<face_descriptor> > faces_per_cc(nb_cc); std::vector< std::set<face_descriptor> > faces_per_cc(nb_cc);
std::vector< bool > exploration_finished(nb_cc, false); std::vector< bool > exploration_finished(nb_cc, false);
// init stacks
// init the stacks of halfedges using the cc of the boundary
std::size_t index=0; std::size_t index=0;
BOOST_FOREACH( halfedge_descriptor hd, ccs) std::map< halfedge_descriptor, std::size_t > ccs;
typedef std::pair<const halfedge_descriptor, typename UF_ds::handle> Pair_type;
BOOST_FOREACH(Pair_type p, handles)
{ {
halfedge_descriptor opp_hedge = opposite(hd, tmesh); halfedge_descriptor opp_hedge = opposite(p.first, tmesh);
stacks_per_cc[ index ].push_back( prev(opp_hedge, tmesh) ); if (is_border(opp_hedge, tmesh)) continue; // nothing to do on the boundary
stacks_per_cc[ index ].push_back( next(opp_hedge, tmesh) );
faces_per_cc[ index ].insert( face(opp_hedge, tmesh) ); typedef typename std::map< halfedge_descriptor, std::size_t >::iterator Map_it;
++index; std::pair<Map_it, bool> insert_res=
ccs.insert( std::make_pair(*uf.find( p.second ), index) );
if (insert_res.second) ++index;
stacks_per_cc[ insert_res.first->second ].push_back( prev(opp_hedge, tmesh) );
stacks_per_cc[ insert_res.first->second ].push_back( next(opp_hedge, tmesh) );
faces_per_cc[ insert_res.first->second ].insert( face(opp_hedge, tmesh) );
} }
std::size_t nb_ccs_to_be_explored = nb_cc; std::size_t nb_ccs_to_be_explored = nb_cc;
@ -424,7 +321,7 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
halfedge_descriptor hd = stacks_per_cc[index].back(); halfedge_descriptor hd = stacks_per_cc[index].back();
stacks_per_cc[index].pop_back(); stacks_per_cc[index].pop_back();
hd = opposite(hd, tmesh); hd = opposite(hd, tmesh);
if ( !marked_faces.count(face(hd, tmesh) ) ) if ( !is_border(hd,tmesh) && !marked_faces.count(face(hd, tmesh) ) )
{ {
if ( faces_per_cc[index].insert( face(hd, tmesh) ).second ) if ( faces_per_cc[index].insert( face(hd, tmesh) ).second )
{ {
@ -522,53 +419,80 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
if ( traits.equal_3_object()(get(vpmap, target(hd, tmesh)), get(vpmap, source(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)); null_edges_to_remove.insert(edge(hd, tmesh));
#if 0 CGAL_assertion( is_valid(tmesh) );
// the following test filters in particular cases where the }
// detection of the vertex that breaks the link condition }
// is not correctly detected by find_larger_triangle
// (i.e. when the hedges detected should be used with the opposite edge) return nb_deg_faces;
if ( traits.collinear_3_object()( }
get(vpmap, target(h, tmesh)),
get(vpmap, target(next(h, tmesh), tmesh)), template <class EdgeRange, class TriangleMesh>
get(vpmap, target(next(opposite(h, tmesh), tmesh), tmesh)) ) ) std::size_t remove_null_edges(
const EdgeRange& edge_range,
TriangleMesh& tmesh)
{ {
// we handle this case in the next loop removing set of degenerated return remove_null_edges(edge_range, tmesh,
// triangles which points are collinear parameters::all_default());
edges_to_remove_skipped.insert(ed);
continue;
} }
do{ /// \ingroup PkgPolygonMeshProcessing
// improve the link condition on h's side /// removes the degenerate faces from a triangulated surface mesh.
boost::optional< std::pair<halfedge_descriptor, halfedge_descriptor> > /// A face is considered degenerate if two of its vertices share the same location,
res = internal::find_larger_triangle(h, tmesh, vpmap, traits); /// or more generally if all its vertices are collinear.
if (res) ///
internal::remove_faces_inside_triangle(res->first, h, res->second, tmesh, null_edges_to_remove, edges_to_remove_skipped); /// @pre `CGAL::is_triangle_mesh(tmesh)`
///
/// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph`
/// that has an internal property map for `boost::vertex_point_t`
/// @tparam NamedParameters a sequence of \ref namedparameters
///
/// @param tmesh the triangulated surface mesh to be repaired
/// @param np optional \ref namedparameters 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` \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
/// - for each functor Foo, a function `Foo foo_object()`
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
/// \return number of removed degenerate faces
/// \endcond
template <class TriangleMesh, class NamedParameters>
std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
const NamedParameters& np)
{
CGAL_assertion(CGAL::is_triangle_mesh(tmesh));
// improve the link condition on opposite(h)'s side using boost::choose_const_pmap;
h=opposite(h, tmesh); using boost::get_param;
res = internal::find_larger_triangle(h, tmesh, vpmap, traits); using boost::choose_param;
if (res)
internal::remove_faces_inside_triangle(res->first, h, res->second, tmesh, null_edges_to_remove, edges_to_remove_skipped);
h=opposite(h, tmesh);
ed=edge(h, tmesh);
} while(!CGAL::Euler::does_satisfy_link_condition(ed,tmesh)); typedef TriangleMesh TM;
typedef typename boost::graph_traits<TriangleMesh> GT;
typedef typename GT::edge_descriptor edge_descriptor;
typedef typename GT::halfedge_descriptor halfedge_descriptor;
typedef typename GT::face_descriptor face_descriptor;
typedef typename GT::vertex_descriptor vertex_descriptor;
null_edges_to_remove.insert( ed ); typedef typename GetVertexPointMap<TM, NamedParameters>::type VertexPointMap;
#endif VertexPointMap vpmap = choose_pmap(get_param(np, boost::vertex_point),
} tmesh,
} boost::vertex_point);
typedef typename GetGeomTraits<TM, NamedParameters>::type Traits;
Traits traits = choose_param(get_param(np, geom_traits), Traits());
// check if some edges were skipped due to link condition not satisfied // First remove edges of length 0
// that could now be satisfied std::size_t nb_deg_faces = remove_null_edges(edges(tmesh), tmesh, np);
if (edges_to_remove_skipped.empty() ||
edges_to_remove_skipped.size()==nb_edges_previously_skipped) break;
null_edges_to_remove.swap(edges_to_remove_skipped);
nb_edges_previously_skipped = null_edges_to_remove.size();
} while(true);
// remove triangles made of 3 collinear points // Then, remove triangles made of 3 collinear points
std::set<face_descriptor> degenerate_face_set; std::set<face_descriptor> degenerate_face_set;
BOOST_FOREACH(face_descriptor fd, faces(tmesh)) BOOST_FOREACH(face_descriptor fd, faces(tmesh))
if ( is_degenerated(fd, tmesh, vpmap, traits) ) if ( is_degenerated(fd, tmesh, vpmap, traits) )

View File

@ -0,0 +1,16 @@
OFF
7 7 0
1 0 0
0 1 0
1 1 0
2 1 0
1 1 0
1 2 0
0.5 1 0
3 0 2 1
3 0 3 2
3 4 3 5
3 1 4 5
3 1 2 6
3 6 2 4
3 6 4 1

View File

@ -32,6 +32,7 @@ int main()
fix("data_degeneracies/degtri_on_border.off"); fix("data_degeneracies/degtri_on_border.off");
fix("data_degeneracies/degtri_three.off"); fix("data_degeneracies/degtri_three.off");
fix("data_degeneracies/degtri_single.off"); fix("data_degeneracies/degtri_single.off");
fix("data_degeneracies/trihole.off");
return 0; return 0;
} }