Merge pull request #2220 from maxGimeno/BGL_expand_face_selection_for_manifold_removal-GF

BGL expand face selection for manifold removal
This commit is contained in:
Sebastien Loriot 2017-08-03 17:09:14 +02:00 committed by GitHub
commit aef9029e46
7 changed files with 224 additions and 8 deletions

View File

@ -27,6 +27,7 @@
#include <CGAL/boost/graph/properties.h>
#include <CGAL/boost/graph/internal/Has_member_clear.h>
#include <CGAL/function_objects.h>
#include <boost/unordered_set.hpp>
namespace CGAL {
@ -1174,7 +1175,6 @@ bool is_empty(const FaceGraph& g)
return boost::empty(vertices(g));
}
} // namespace CGAL
// Include "Euler_operations.h" at the end, because its implementation

View File

@ -23,6 +23,7 @@
#include <boost/graph/graph_traits.hpp>
#include <boost/foreach.hpp>
#include <CGAL/boost/graph/iterator.h>
#include <boost/unordered_set.hpp>
namespace CGAL {
@ -521,6 +522,102 @@ reduce_vertex_selection(
return out;
}
/**
* \ingroup PkgBGLSelectionFct
*
* Expands a selection of faces so that their removal does not create any non manifold vertex.
* For each vertex that is incident to a selected face, we turn around that vertex and check
* if there is exactly one set of consecutive selected faces. If not, additional faces around
* that vertex are selected to match this condition.
*
* @tparam TriangleMesh a model of `FaceGraph` that is triangulated.
* @tparam FaceRange a range of `boost::graph_traits<TriangleMesh>::%face_descriptor`,
* with an iterator type model of `ForwardIterator`.
* @tparam IsSelectedMap a model of `ReadWritePropertyMap` with
* `boost::graph_traits<TriangleMesh>::%face_descriptor` as key and `bool` as value.
* @param tm the triangle mesh.
* @param faces_to_be_deleted the range of selected faces.
* @param is_selected a property map containing the selected-or-not status of each face of `tm`.
* It must associate `true` to each face of `faces_to_be_deleted` and `false` to any other face of `tm`.
* It will be modified if the face selection must be expanded.
*
**/
template<class TriangleMesh, class FaceRange, class IsSelectedMap>
void expand_face_selection_for_removal(TriangleMesh& tm,
const FaceRange& faces_to_be_deleted,
IsSelectedMap is_selected)
{
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
boost::unordered_set<vertex_descriptor> vertices_queue;
// collect vertices belonging to at least a triangle that will be removed
BOOST_FOREACH(face_descriptor fd, faces_to_be_deleted)
{
halfedge_descriptor h = halfedge(fd, tm);
vertices_queue.insert( target(h, tm) );
vertices_queue.insert( target(next(h, tm), tm) );
vertices_queue.insert( target(prev(h, tm), tm) );
}
while (!vertices_queue.empty())
{
vertex_descriptor vd = *vertices_queue.begin();
vertices_queue.erase( vertices_queue.begin() );
// set hd to the last selected face of a connected component
// of selected faces around a vertex
halfedge_descriptor hd = halfedge(vd, tm);
while( !get(is_selected, face(hd, tm)) )
{
hd = opposite( next(hd, tm), tm);
CGAL_assertion( hd != halfedge(vd, tm) );
}
halfedge_descriptor start = hd;
halfedge_descriptor next_around_vertex = opposite( next(hd, tm), tm);
while( get(is_selected, face(next_around_vertex, tm) ) )
{
hd = next_around_vertex;
next_around_vertex = opposite( next(hd, tm), tm);
if (hd==start) break;
}
if ( get(is_selected, face(next_around_vertex, tm) ) ) continue; //all incident faces will be removed
while( true )
{
// collect non-selected faces
std::vector<halfedge_descriptor> faces_traversed;
do
{
faces_traversed.push_back(next_around_vertex);
next_around_vertex = opposite( next(next_around_vertex, tm), tm);
}
while( !get(is_selected, face(next_around_vertex, tm) ) );
// go over the connected components of faces to remove
do{
if (next_around_vertex==start)
break;
next_around_vertex = opposite( next(next_around_vertex, tm), tm);
}
while( get(is_selected, face(next_around_vertex, tm) ) );
if (next_around_vertex==start)
break;
BOOST_FOREACH(halfedge_descriptor f_hd, faces_traversed)
{
assert(target(f_hd, tm) == vd);
put(is_selected, face(f_hd, tm), true);
vertices_queue.insert( target( next(f_hd, tm), tm) );
vertices_queue.insert( source(f_hd, tm) );
}
}
}
}
} //end of namespace CGAL
#endif //CGAL_BOOST_GRAPH_KTH_SIMPLICIAL_NEIGHBORHOOD_H

View File

@ -95,6 +95,8 @@ create_single_source_cgal_program( "test_bgl_read_write.cpp" )
create_single_source_cgal_program( "graph_concept_Face_filtered_graph.cpp" )
create_single_source_cgal_program( "test_Manifold_face_removal.cpp")
create_single_source_cgal_program( "test_Face_filtered_graph.cpp" )
create_single_source_cgal_program( "test_Euler_operations.cpp" )

View File

@ -0,0 +1,62 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/boost/graph/graph_traits_Surface_mesh.h>
#include <CGAL/Surface_mesh/IO.h>
#include <boost/unordered_map.hpp>
#include <boost/property_map/property_map.hpp>
#include <iostream>
#include <fstream>
#include <set>
#include <CGAL/boost/graph/selection.h>
#include <CGAL/boost/graph/helpers.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef CGAL::Surface_mesh<Kernel::Point_3> SM;
typedef boost::graph_traits<SM>::face_descriptor face_descriptor;
int main()
{
SM sm;
std::ifstream input("data/head.off");
input >> sm;
// define my selection of faces to remove
boost::unordered_map<face_descriptor, bool> is_selected_map;
const int selection_indices[] = {501, 652, 646, 322, 328, 212, 347, 345, 352, 353, 696, 697, 698, 706, 714, 2892};
std::set<int> index_set(&selection_indices[0], &selection_indices[0]+16);
std::vector<face_descriptor> faces_to_remove;
int index = 0;
BOOST_FOREACH(face_descriptor fh, faces(sm))
{
if(index_set.count(index)==0)
is_selected_map[fh]=false;
else
{
faces_to_remove.push_back(fh);
is_selected_map[fh]=true;
}
++index;
}
expand_face_selection_for_removal(sm,
faces_to_remove,
boost::make_assoc_property_map(is_selected_map));
index=0;
BOOST_FOREACH(face_descriptor fh, faces(sm))
{
if (is_selected_map[fh])
{
CGAL::Euler::remove_face(halfedge(fh, sm), sm);
++index;
}
}
assert(index == 25);
assert(is_valid(sm));
return 0;
}

View File

@ -155,8 +155,15 @@ and <code>src/</code> directories).
<!-- Kinetic Data Structures -->
<!-- Support Library -->
<!-- Visualization -->
<!-- end of the div for 4.10 -->
<!-- Support Library -->
<h3>CGAL and the Boost Graph Library (BGL)</h3>
<ul>
<li>
Add helper function <code>CGAL::expand_face_selection_for_removal</code> that
expands a face selection to avoid creating a non manifold mesh when removing the selected faces.
</li>
</ul>
<!-- end of the div for 4.12 -->
</div>

View File

@ -491,8 +491,44 @@ public Q_SLOTS:
selection_item->keep_connected_components();
break;
}
//Convert from Edge Selection to Facet Selection
//Expand face selection
case 5:
{
Scene_polyhedron_selection_item* selection_item = getSelectedItem<Scene_polyhedron_selection_item>();
if (!selection_item ||
selection_item->selected_facets.empty())
{
print_message("Error: Please select a selection item with a selection of faces.");
return;
}
boost::unordered_map<fg_face_descriptor, bool> is_selected_map;
int index = 0;
BOOST_FOREACH(fg_face_descriptor fh, faces(*selection_item->polyhedron()))
{
if(selection_item->selected_facets.find(fh)
== selection_item->selected_facets.end())
is_selected_map[fh]=false;
else
{
is_selected_map[fh]=true;
}
++index;
}
CGAL::expand_face_selection_for_removal(*selection_item->polyhedron(),
selection_item->selected_facets,
boost::make_assoc_property_map(is_selected_map));
BOOST_FOREACH(fg_face_descriptor fh, faces(*selection_item->polyhedron()))
{
if (is_selected_map[fh])
selection_item->selected_facets.insert(fh);
}
selection_item->invalidateOpenGLBuffers();
selection_item->itemChanged();
break;
}
//Convert from Edge Selection to Facet Selection
case 6:
{
Scene_polyhedron_selection_item* selection_item = getSelectedItem<Scene_polyhedron_selection_item>();
if(!selection_item) {
@ -514,7 +550,7 @@ public Q_SLOTS:
break;
}
//Convert from Edge Selection to Point Selection
case 6:
case 7:
{
Scene_polyhedron_selection_item* selection_item = getSelectedItem<Scene_polyhedron_selection_item>();
if(!selection_item) {
@ -537,7 +573,7 @@ public Q_SLOTS:
break;
}
//Convert from Facet Selection to Bounding Edge Selection
case 7:
case 8:
{
Scene_polyhedron_selection_item* selection_item = getSelectedItem<Scene_polyhedron_selection_item>();
if(!selection_item) {
@ -560,7 +596,7 @@ public Q_SLOTS:
break;
}
//Convert from Facet Selection to Points Selection
case 8:
case 9:
{
Scene_polyhedron_selection_item* selection_item = getSelectedItem<Scene_polyhedron_selection_item>();
if(!selection_item) {

View File

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>597</width>
<width>613</width>
<height>334</height>
</rect>
</property>
@ -429,6 +429,13 @@
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton">
<property name="text">
<string>PushButton</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_8">
<property name="orientation">
@ -475,6 +482,11 @@
<string>Keep Connected Components of Selected Facets</string>
</property>
</item>
<item>
<property name="text">
<string>Expand Face Selection to Stay Manifold After Removal</string>
</property>
</item>
<item>
<property name="text">
<string>Convert from Edge Selection to Facets Selection</string>