mirror of https://github.com/CGAL/cgal
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:
commit
aef9029e46
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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" )
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
@ -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>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue