Merge pull request #3884 from MaelRL/BGL-Fix_FFG_master-GF

BGL: Fix `Face_filtered_graph`'s `is_selection_valid()` (5.0)
This commit is contained in:
Laurent Rineau 2019-05-06 15:30:33 +02:00
commit d466ea50cd
2 changed files with 114 additions and 57 deletions

View File

@ -463,64 +463,57 @@ struct Face_filtered_graph
/// returns `true` if around any vertex of a selected face,
/// there is at most one connected set of selected faces.
bool is_selection_valid()
bool is_selection_valid() const
{
for(vertex_descriptor vd : vertices(*this) )
typedef typename boost::graph_traits<Graph>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<Graph>::halfedge_descriptor halfedge_descriptor;
// Non-manifoldness can appear either:
// - if 'pm' is pinched at a vertex. While traversing the incoming halfedges at this vertex,
// we will meet strictly more than one border halfedge.
// - if there are multiple umbrellas around a vertex. In that case, we will find a non-visited
// halfedge that has for target a vertex that is already visited.
boost::unordered_set<vertex_descriptor> vertices_visited;
boost::unordered_set<halfedge_descriptor> halfedges_handled;
for(halfedge_descriptor hd : halfedges(*this))
{
face_descriptor first_selected = boost::graph_traits<Graph>::null_face();
bool first_unselected_found(false),
second_unselected_found(false);
CGAL_assertion(is_in_cc(hd));
//find an unselected face, then find the first selected face.
//Find another unselected face, the next selected face must be the first;
//else this is not valid.
halfedge_descriptor hd = halfedge(vd, _graph);
face_descriptor first_tested = boost::graph_traits<Graph>::null_face();
while(1) //will break if valid, return false if not valid
if(!halfedges_handled.insert(hd).second) // already treated this halfedge
continue;
vertex_descriptor vd = target(hd, *this);
CGAL_assertion(is_in_cc(vd));
// Check if we have already met this vertex before (necessarily in a different umbrella
// since we have never treated the halfedge 'hd')
if(!vertices_visited.insert(vd).second)
return false;
std::size_t border_halfedge_counter = 0;
// Can't simply call halfedges_around_target(vd, *this) because 'halfedge(vd)' is not necessarily 'hd'
halfedge_descriptor ihd = hd;
do
{
face_descriptor fd = face(hd, _graph);
halfedges_handled.insert(ihd);
if(is_border(ihd, *this))
++border_halfedge_counter;
if(first_tested == boost::graph_traits<Graph>::null_face())
first_tested = fd;
else if(fd == first_tested )
do
{
//if there is no unselected face, break
if(selected_faces[get(fimap, fd)] && !first_unselected_found)
break;
//if there is no selected face, break
else if(!selected_faces[get(fimap, fd)] &&
first_selected == boost::graph_traits<Graph>::null_face())
break;
ihd = prev(opposite(ihd, _graph), _graph);
}
if(fd != boost::graph_traits<Graph>::null_face())
{
if(selected_faces[get(fimap, fd)])
{
if(first_unselected_found &&
first_selected == boost::graph_traits<Graph>::null_face())
{
first_selected = fd;
}
else if(second_unselected_found)
{
if(fd == first_selected)
break;
else
return false;
}
}
else
{
if(first_selected == boost::graph_traits<Graph>::null_face())
first_unselected_found = true;
else
second_unselected_found = true;
}
}
hd = next(opposite(hd, _graph), _graph);
while(!is_in_cc(ihd) && ihd != hd);
}
while(ihd != hd);
if(border_halfedge_counter > 1)
return false;
}
return true;
}

View File

@ -268,17 +268,20 @@ void test_read(const Graph& g)
typedef typename boost::graph_traits<Graph>::face_descriptor g_face_descriptor;
typedef CGAL::Face_filtered_graph<Graph> Adapter;
CGAL_GRAPH_TRAITS_MEMBERS(Adapter);
std::map<g_face_descriptor, std::size_t> map;
CGAL::Polygon_mesh_processing::connected_components(g, boost::make_assoc_property_map(map), CGAL::Polygon_mesh_processing::parameters::all_default());
Adapter fg(g, 0, boost::make_assoc_property_map(map));
assert(fg.is_selection_valid());
assert(CGAL::is_valid_polygon_mesh(fg));
}
template <typename Graph>
void
test(const std::vector<Graph>& graphs)
test_graph_range(const std::vector<Graph>& graphs)
{
for(Graph p : graphs){
for(Graph p : graphs)
{
test_read(p);
test_vertex_iterators(p);
test_out_edges(p);
@ -392,14 +395,75 @@ void test_mesh(Adapter fga)
CGAL::copy_face_graph(fga, copy);
}
int
main()
template <typename PolygonMesh>
void merge_vertices(typename boost::graph_traits<PolygonMesh>::vertex_descriptor v_keep,
typename boost::graph_traits<PolygonMesh>::vertex_descriptor v_rm,
std::vector<typename boost::graph_traits<PolygonMesh>::face_descriptor>& incident_faces,
PolygonMesh& mesh)
{
test(sm_data());
#ifdef CGAL_USE_OPENMESH
test(omesh_data());
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
halfedge_descriptor oh = halfedge(v_keep, mesh), done = oh;
do
{
incident_faces.push_back(face(oh, mesh));
oh = opposite(next(oh, mesh), mesh);
}
while(oh != done);
halfedge_descriptor h = halfedge(v_rm, mesh);
halfedge_descriptor start = h;
do
{
set_target(h, v_keep, mesh);
incident_faces.push_back(face(h, mesh));
h = opposite(next(h, mesh), mesh);
}
while(h != start);
remove_vertex(v_rm, mesh);
}
void test_invalid_selections()
{
// this creates a non-manifold (pinched) vertex
SM mesh;
read_a_mesh(mesh, "data/7_faces_triangle.off");
std::vector<SM::Face_index> face_range;
face_range.push_back(SM::Face_index(1));
face_range.push_back(SM::Face_index(2));
face_range.push_back(SM::Face_index(3));
CGAL::Face_filtered_graph<SM> bad_fg(mesh, face_range);
assert(!bad_fg.is_selection_valid());
// this creates a non-manifold vertex (multiple umbrellas)
clear(mesh);
read_a_mesh(mesh, "data/genus3.off");
assert(is_valid_polygon_mesh(mesh));
face_range.clear();
merge_vertices(SM::Vertex_index(1337), SM::Vertex_index(87), face_range, mesh);
CGAL::Face_filtered_graph<SM> bad_fg_2(mesh, face_range);
assert(!bad_fg_2.is_selection_valid());
}
int main()
{
test_graph_range(poly_data());
#if defined(CGAL_USE_SURFACE_MESH)
test_graph_range(sm_data());
#endif
#ifdef CGAL_USE_OPENMESH
test_graph_range(omesh_data());
#endif
test_invalid_selections();
//Make a tetrahedron and test the adapter for a patch that only contains 2 faces
typedef CGAL::Face_filtered_graph<SM> SM_Adapter;
typedef SM::Property_map<boost::graph_traits<SM>::face_descriptor , std::size_t> SM_FCCMap;