mirror of https://github.com/CGAL/cgal
Resolve conflicts between master and 4.14-based branch that fixes manifoldness
Conflicts resulting from the switch to C++11 (BOOST_FOREACH disappearing etc.)
This commit is contained in:
parent
7268743b70
commit
713f5a2d45
|
|
@ -368,7 +368,7 @@ should be considered as part of the clipping volume or not.
|
|||
Parameter to pass an output iterator.
|
||||
\n
|
||||
\b Type : a model of `OutputIterator` \n
|
||||
\b Default : `Emptyset_iterator`
|
||||
\b Default : `#CGAL::Emptyset_iterator`
|
||||
\cgalNPEnd
|
||||
|
||||
\cgalNPBegin{erase_all_duplicates} \anchor PMP_erase_all_duplicates
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@
|
|||
// SPDX-License-Identifier: GPL-3.0+
|
||||
//
|
||||
//
|
||||
// Author(s) : Sebastien Loriot
|
||||
// Author(s) : Sebastien Loriot,
|
||||
// Mael Rouxel-Labbé
|
||||
|
||||
#ifndef CGAL_POLYGON_MESH_PROCESSING_REPAIR_H
|
||||
#define CGAL_POLYGON_MESH_PROCESSING_REPAIR_H
|
||||
|
|
@ -25,6 +26,7 @@
|
|||
#include <CGAL/license/Polygon_mesh_processing/repair.h>
|
||||
|
||||
#include <CGAL/boost/graph/Euler_operations.h>
|
||||
#include <CGAL/Dynamic_property_map.h>
|
||||
#include <CGAL/Union_find.h>
|
||||
#include <CGAL/property_map.h>
|
||||
#include <CGAL/algorithm.h>
|
||||
|
|
@ -1949,45 +1951,6 @@ bool remove_degenerate_faces(TriangleMesh& tmesh)
|
|||
CGAL::Polygon_mesh_processing::parameters::all_default());
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename G, typename OutputIterator>
|
||||
struct Vertex_collector
|
||||
{
|
||||
typedef typename boost::graph_traits<G>::vertex_descriptor vertex_descriptor;
|
||||
|
||||
void collect_vertices(vertex_descriptor v1, vertex_descriptor v2)
|
||||
{
|
||||
std::vector<vertex_descriptor>& verts = collections[v1];
|
||||
if(verts.empty())
|
||||
verts.push_back(v1);
|
||||
verts.push_back(v2);
|
||||
}
|
||||
|
||||
void dump(OutputIterator out)
|
||||
{
|
||||
typedef std::pair<const vertex_descriptor, std::vector<vertex_descriptor> > Pair_type;
|
||||
for(const Pair_type& p : collections) {
|
||||
*out++ = p.second;
|
||||
}
|
||||
}
|
||||
|
||||
std::map<vertex_descriptor, std::vector<vertex_descriptor> > collections;
|
||||
};
|
||||
|
||||
template <typename G>
|
||||
struct Vertex_collector<G, Emptyset_iterator>
|
||||
{
|
||||
typedef typename boost::graph_traits<G>::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 polygon mesh is non-manifold.
|
||||
///
|
||||
|
|
@ -2004,12 +1967,19 @@ bool is_non_manifold_vertex(typename boost::graph_traits<PolygonMesh>::vertex_de
|
|||
const PolygonMesh& pm)
|
||||
{
|
||||
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
|
||||
boost::unordered_set<halfedge_descriptor> halfedges_handled;
|
||||
|
||||
typedef CGAL::dynamic_halfedge_property_t<bool> Halfedge_property_tag;
|
||||
typedef typename boost::property_map<PolygonMesh, Halfedge_property_tag>::const_type Visited_halfedge_map;
|
||||
|
||||
// Dynamic pmaps do not have default initialization values (yet)
|
||||
Visited_halfedge_map visited_halfedges = get(Halfedge_property_tag(), pm);
|
||||
for(halfedge_descriptor h : halfedges(pm))
|
||||
put(visited_halfedges, h, false);
|
||||
|
||||
std::size_t incident_null_faces_counter = 0;
|
||||
for(halfedge_descriptor h : halfedges_around_target(v, pm))
|
||||
{
|
||||
halfedges_handled.insert(h);
|
||||
put(visited_halfedges, h, true);
|
||||
if(CGAL::is_border(h, pm))
|
||||
++incident_null_faces_counter;
|
||||
}
|
||||
|
|
@ -2024,8 +1994,8 @@ bool is_non_manifold_vertex(typename boost::graph_traits<PolygonMesh>::vertex_de
|
|||
{
|
||||
if(v == target(h, pm))
|
||||
{
|
||||
// More than one umbrella incident to 'v' --> non-manifold
|
||||
if(halfedges_handled.count(h) == 0)
|
||||
// Haven't seen that halfedge yet ==> more than one umbrella incident to 'v' ==> non-manifold
|
||||
if(!get(visited_halfedges, h))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -2033,20 +2003,193 @@ bool is_non_manifold_vertex(typename boost::graph_traits<PolygonMesh>::vertex_de
|
|||
return false;
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename G>
|
||||
struct Vertex_collector
|
||||
{
|
||||
typedef typename boost::graph_traits<G>::vertex_descriptor vertex_descriptor;
|
||||
|
||||
bool has_old_vertex(const vertex_descriptor v) const { return collections.count(v) != 0; }
|
||||
|
||||
void collect_vertices(vertex_descriptor v1, vertex_descriptor v2)
|
||||
{
|
||||
std::vector<vertex_descriptor>& verts = collections[v1];
|
||||
if(verts.empty())
|
||||
verts.push_back(v1);
|
||||
verts.push_back(v2);
|
||||
}
|
||||
|
||||
template<typename OutputIterator>
|
||||
void dump(OutputIterator out)
|
||||
{
|
||||
typedef std::pair<const vertex_descriptor, std::vector<vertex_descriptor> > Pair_type;
|
||||
for(const Pair_type& p : collections)
|
||||
*out++ = p.second;
|
||||
}
|
||||
|
||||
void dump(Emptyset_iterator) { }
|
||||
|
||||
std::map<vertex_descriptor, std::vector<vertex_descriptor> > collections;
|
||||
};
|
||||
|
||||
} // end namespace internal
|
||||
|
||||
template <typename PolygonMesh, typename VPM, typename ConstraintMap>
|
||||
typename boost::graph_traits<PolygonMesh>::vertex_descriptor
|
||||
create_new_vertex_for_sector(typename boost::graph_traits<PolygonMesh>::halfedge_descriptor sector_begin_h,
|
||||
typename boost::graph_traits<PolygonMesh>::halfedge_descriptor sector_last_h,
|
||||
PolygonMesh& pm,
|
||||
const VPM& vpm,
|
||||
const ConstraintMap& cmap)
|
||||
{
|
||||
typedef typename boost::graph_traits<PolygonMesh>::vertex_descriptor vertex_descriptor;
|
||||
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
|
||||
|
||||
vertex_descriptor old_vd = target(sector_begin_h, pm);
|
||||
vertex_descriptor new_vd = add_vertex(pm);
|
||||
put(vpm, new_vd, get(vpm, old_vd));
|
||||
|
||||
put(cmap, new_vd, true);
|
||||
|
||||
set_halfedge(new_vd, sector_begin_h, pm);
|
||||
halfedge_descriptor h = sector_begin_h;
|
||||
do
|
||||
{
|
||||
set_target(h, new_vd, pm);
|
||||
|
||||
if(h == sector_last_h)
|
||||
break;
|
||||
else
|
||||
h = prev(opposite(h, pm), pm);
|
||||
}
|
||||
while(h != sector_begin_h); // for safety
|
||||
CGAL_assertion(h != sector_begin_h);
|
||||
|
||||
return new_vd;
|
||||
}
|
||||
|
||||
template <typename PolygonMesh, typename NamedParameters>
|
||||
std::size_t make_umbrella_manifold(typename boost::graph_traits<PolygonMesh>::halfedge_descriptor h,
|
||||
PolygonMesh& pm,
|
||||
internal::Vertex_collector<PolygonMesh>& dmap,
|
||||
const NamedParameters& np)
|
||||
{
|
||||
typedef typename boost::graph_traits<PolygonMesh>::vertex_descriptor vertex_descriptor;
|
||||
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
|
||||
|
||||
typedef typename GetVertexPointMap<PolygonMesh, NamedParameters>::type VertexPointMap;
|
||||
VertexPointMap vpm = choose_param(get_param(np, internal_np::vertex_point),
|
||||
get_property_map(vertex_point, pm));
|
||||
|
||||
typedef typename boost::lookup_named_param_def<internal_np::vertex_is_constrained_t,
|
||||
NamedParameters,
|
||||
Constant_property_map<vertex_descriptor, bool> // default (no constraint pmap)
|
||||
>::type VerticesMap;
|
||||
VerticesMap cmap = choose_param(get_param(np, internal_np::vertex_is_constrained),
|
||||
Constant_property_map<vertex_descriptor, bool>(false));
|
||||
|
||||
std::size_t nb_new_vertices = 0;
|
||||
|
||||
vertex_descriptor old_v = target(h, pm);
|
||||
put(cmap, old_v, true); // store the duplicates
|
||||
|
||||
// count the number of borders
|
||||
int border_counter = 0;
|
||||
halfedge_descriptor ih = h, done = ih, border_h = h;
|
||||
do
|
||||
{
|
||||
if(is_border(ih, pm))
|
||||
{
|
||||
border_h = ih;
|
||||
++border_counter;
|
||||
}
|
||||
|
||||
ih = prev(opposite(ih, pm), pm);
|
||||
}
|
||||
while(ih != done);
|
||||
|
||||
bool is_non_manifold_within_umbrella = (border_counter > 1);
|
||||
|
||||
// if there is a single sector, then simply move the full umbrella to a new vertex, and we're done
|
||||
if(!is_non_manifold_within_umbrella)
|
||||
{
|
||||
// note that since this is marked as a non-manifold vertex, we necessarily need to create
|
||||
// a new vertex for this umbrella (the main umbrella is not marked as non-manifold)
|
||||
halfedge_descriptor last_h = opposite(next(h, pm), pm);
|
||||
vertex_descriptor new_v = create_new_vertex_for_sector(h, last_h, pm, vpm, cmap);
|
||||
dmap.collect_vertices(old_v, new_v);
|
||||
nb_new_vertices = 1;
|
||||
}
|
||||
// if there is more than one sector, look at each sector and split them away from the main one
|
||||
else
|
||||
{
|
||||
// the first manifold sector, described by two halfedges
|
||||
halfedge_descriptor sector_start_h = border_h;
|
||||
CGAL_assertion(is_border(border_h, pm));
|
||||
|
||||
bool should_stop = false;
|
||||
bool is_main_sector = true;
|
||||
do
|
||||
{
|
||||
CGAL_assertion(is_border(sector_start_h, pm));
|
||||
|
||||
// collect the sector and split it away if it must be
|
||||
halfedge_descriptor sector_last_h = sector_start_h;
|
||||
do
|
||||
{
|
||||
halfedge_descriptor next_h = prev(opposite(sector_last_h, pm), pm);
|
||||
|
||||
if(is_border(next_h, pm))
|
||||
break;
|
||||
|
||||
sector_last_h = next_h;
|
||||
}
|
||||
while(sector_last_h != sector_start_h);
|
||||
CGAL_assertion(!is_border(sector_last_h, pm));
|
||||
CGAL_assertion(sector_last_h != sector_start_h);
|
||||
|
||||
halfedge_descriptor next_start_h = prev(opposite(sector_last_h, pm), pm);
|
||||
|
||||
// there are multiple CCs incident to this particular vertex, and we should create a new vertex
|
||||
// if it's not the first umbrella around 'old_v' or not the first sector, but not if it's
|
||||
// the first umbrella and first sector.
|
||||
bool must_create_new_vertex = (!is_main_sector || dmap.has_old_vertex(old_v));
|
||||
|
||||
// In any case, we must set up the next pointer correctly
|
||||
set_next(sector_start_h, opposite(sector_last_h, pm), pm);
|
||||
|
||||
if(must_create_new_vertex)
|
||||
{
|
||||
vertex_descriptor new_v = create_new_vertex_for_sector(sector_start_h, sector_last_h, pm, vpm, cmap);
|
||||
dmap.collect_vertices(old_v, new_v);
|
||||
++nb_new_vertices;
|
||||
}
|
||||
|
||||
is_main_sector = false;
|
||||
sector_start_h = next_start_h;
|
||||
should_stop = (sector_start_h == border_h);
|
||||
}
|
||||
while(!should_stop);
|
||||
}
|
||||
|
||||
return nb_new_vertices;
|
||||
}
|
||||
|
||||
/// \ingroup PMP_repairing_grp
|
||||
/// duplicates all the non-manifold vertices of the input mesh.
|
||||
///
|
||||
/// @tparam TriangleMesh a model of `HalfedgeListGraph` and `MutableHalfedgeGraph`
|
||||
/// @tparam PolygonMesh 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 pm the 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`
|
||||
/// `CGAL::vertex_point_t` should be available in `PolygonMesh`
|
||||
/// \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
|
||||
|
|
@ -2060,32 +2203,17 @@ bool is_non_manifold_vertex(typename boost::graph_traits<PolygonMesh>::vertex_de
|
|||
/// \cgalNamedParamsEnd
|
||||
///
|
||||
/// \return the number of vertices created.
|
||||
template <typename TriangleMesh, typename NamedParameters>
|
||||
std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm,
|
||||
template <typename PolygonMesh, typename NamedParameters>
|
||||
std::size_t duplicate_non_manifold_vertices(PolygonMesh& pm,
|
||||
const NamedParameters& np)
|
||||
{
|
||||
CGAL_assertion(CGAL::is_triangle_mesh(tm));
|
||||
|
||||
using boost::get_param;
|
||||
using boost::choose_param;
|
||||
|
||||
typedef boost::graph_traits<TriangleMesh> GT;
|
||||
typedef boost::graph_traits<PolygonMesh> GT;
|
||||
typedef typename GT::vertex_descriptor vertex_descriptor;
|
||||
typedef typename GT::halfedge_descriptor halfedge_descriptor;
|
||||
|
||||
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::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<vertex_descriptor, bool> // default (no constraint pmap)
|
||||
> ::type VerticesMap;
|
||||
VerticesMap cmap
|
||||
= choose_param(get_param(np, internal_np::vertex_is_constrained),
|
||||
Constant_property_map<vertex_descriptor, bool>(false));
|
||||
|
||||
typedef typename boost::lookup_named_param_def <
|
||||
internal_np::output_iterator_t,
|
||||
NamedParameters,
|
||||
|
|
@ -2095,70 +2223,78 @@ std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm,
|
|||
= choose_param(get_param(np, internal_np::output_iterator),
|
||||
Emptyset_iterator());
|
||||
|
||||
internal::Vertex_collector<TriangleMesh, Output_iterator> dmap;
|
||||
boost::unordered_set<vertex_descriptor> vertices_handled;
|
||||
boost::unordered_set<halfedge_descriptor> halfedges_handled;
|
||||
internal::Vertex_collector<PolygonMesh> dmap;
|
||||
|
||||
typedef CGAL::dynamic_vertex_property_t<bool> Vertex_property_tag;
|
||||
typedef typename boost::property_map<PolygonMesh, Vertex_property_tag>::type Visited_vertex_map;
|
||||
typedef CGAL::dynamic_halfedge_property_t<bool> Halfedge_property_tag;
|
||||
typedef typename boost::property_map<PolygonMesh, Halfedge_property_tag>::type Visited_halfedge_map;
|
||||
|
||||
Visited_vertex_map visited_vertices = get(Vertex_property_tag(), pm);
|
||||
Visited_halfedge_map visited_halfedges = get(Halfedge_property_tag(), pm);
|
||||
|
||||
// Dynamic pmaps do not have default initialization values (yet)
|
||||
for(vertex_descriptor v : vertices(pm))
|
||||
put(visited_vertices, v, false);
|
||||
for(halfedge_descriptor h : halfedges(pm))
|
||||
put(visited_halfedges, h, false);
|
||||
|
||||
std::size_t nb_new_vertices = 0;
|
||||
|
||||
std::vector<halfedge_descriptor> non_manifold_cones;
|
||||
for(halfedge_descriptor h : halfedges(tm))
|
||||
for(halfedge_descriptor h : halfedges(pm))
|
||||
{
|
||||
// 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)
|
||||
if(!get(visited_halfedges, h))
|
||||
{
|
||||
vertex_descriptor vd = target(h, tm);
|
||||
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);
|
||||
}
|
||||
put(visited_halfedges, h, true);
|
||||
bool is_non_manifold = false;
|
||||
|
||||
halfedge_descriptor start = opposite(next(h, tm), tm);
|
||||
h = start;
|
||||
vertex_descriptor vd = target(h, pm);
|
||||
if(get(visited_vertices, vd)) // already seen this vertex, but not from this star
|
||||
is_non_manifold = true;
|
||||
|
||||
put(visited_vertices, vd, true);
|
||||
|
||||
// While walking the star of this halfedge, if we meet a border halfedge more than once,
|
||||
// it means the mesh is pinched and we are also in the case of a non-manifold situation
|
||||
halfedge_descriptor ih = h, done = ih;
|
||||
int border_counter = 0;
|
||||
do
|
||||
{
|
||||
halfedges_handled.insert(h);
|
||||
h = opposite(next(h, tm), tm);
|
||||
put(visited_halfedges, ih, true);
|
||||
if(is_border(ih, pm))
|
||||
++border_counter;
|
||||
|
||||
ih = prev(opposite(ih, pm), pm);
|
||||
}
|
||||
while(h != start);
|
||||
while(ih != done);
|
||||
|
||||
if(border_counter > 1)
|
||||
is_non_manifold = true;
|
||||
|
||||
if(is_non_manifold)
|
||||
non_manifold_cones.push_back(h);
|
||||
}
|
||||
}
|
||||
|
||||
if(!non_manifold_cones.empty())
|
||||
{
|
||||
for(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
|
||||
{
|
||||
set_target(h, new_vd, tm);
|
||||
h = opposite(next(h, tm), tm);
|
||||
}
|
||||
while(h != start);
|
||||
}
|
||||
nb_new_vertices += make_umbrella_manifold(h, pm, dmap, np);
|
||||
|
||||
dmap.dump(out);
|
||||
}
|
||||
|
||||
return nb_new_vertices;
|
||||
}
|
||||
|
||||
template <class TriangleMesh>
|
||||
std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm)
|
||||
template <class PolygonMesh>
|
||||
std::size_t duplicate_non_manifold_vertices(PolygonMesh& pm)
|
||||
{
|
||||
return duplicate_non_manifold_vertices(tm, parameters::all_default());
|
||||
return duplicate_non_manifold_vertices(pm, parameters::all_default());
|
||||
}
|
||||
|
||||
/// \ingroup PMP_repairing_grp
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ endif()
|
|||
create_single_source_cgal_program("test_shape_predicates.cpp")
|
||||
create_single_source_cgal_program("test_pmp_collision_detection.cpp")
|
||||
create_single_source_cgal_program("remove_degeneracies_test.cpp")
|
||||
create_single_source_cgal_program("test_pmp_manifoldness.cpp")
|
||||
|
||||
if( TBB_FOUND )
|
||||
CGAL_target_use_TBB(test_pmp_distance)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
OFF
|
||||
16 8 0
|
||||
0 0 0
|
||||
1 0 0
|
||||
2 0 0
|
||||
2 1 0
|
||||
0 1 0
|
||||
0 -1 0
|
||||
2 -1 0
|
||||
0 1 2
|
||||
0 -1 1
|
||||
1 0 0
|
||||
1 1 -1
|
||||
2 0 -1
|
||||
1 0 1
|
||||
1 1 0
|
||||
0 1 0
|
||||
1 0 0
|
||||
3 0 1 4
|
||||
3 1 2 3
|
||||
3 5 6 1
|
||||
3 7 8 9
|
||||
3 10 9 11
|
||||
3 12 14 13
|
||||
3 13 14 15
|
||||
3 15 12 13
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
OFF
|
||||
16 24 0
|
||||
0 0 0
|
||||
1 0 0
|
||||
1 1 0
|
||||
0 1 0
|
||||
0 0 1
|
||||
1 0 1
|
||||
1 1 1
|
||||
0 1 1
|
||||
2 0 1
|
||||
2 1 1
|
||||
1 0 2
|
||||
2 0 2
|
||||
2 1 2
|
||||
1 1 2
|
||||
1 0 1
|
||||
1 1 1
|
||||
3 0 2 1
|
||||
3 0 3 2
|
||||
3 0 1 5
|
||||
3 0 5 4
|
||||
3 3 0 4
|
||||
3 3 4 7
|
||||
3 2 3 7
|
||||
3 2 7 6
|
||||
3 4 5 6
|
||||
3 4 6 7
|
||||
3 1 2 5
|
||||
3 5 2 6
|
||||
3 14 9 8
|
||||
3 15 9 14
|
||||
3 14 8 11
|
||||
3 14 11 10
|
||||
3 8 12 11
|
||||
3 8 9 12
|
||||
3 12 9 15
|
||||
3 13 12 15
|
||||
3 15 14 10
|
||||
3 15 10 13
|
||||
3 10 11 12
|
||||
3 10 12 13
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
OFF
|
||||
7 3 0
|
||||
0 0 0
|
||||
1 0 0
|
||||
2 0 0
|
||||
2 1 0
|
||||
0 1 0
|
||||
0 -1 0
|
||||
2 -1 0
|
||||
3 0 1 4
|
||||
3 1 2 3
|
||||
3 5 6 1
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
OFF
|
||||
5 2 0
|
||||
0 0 0
|
||||
1 0 0
|
||||
2 0 0
|
||||
2 1 0
|
||||
0 1 0
|
||||
3 0 1 4
|
||||
3 1 2 3
|
||||
|
|
@ -0,0 +1,272 @@
|
|||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||
|
||||
#include <CGAL/Polyhedron_3.h>
|
||||
#include <CGAL/Surface_mesh.h>
|
||||
|
||||
#include <CGAL/boost/graph/named_params_helper.h>
|
||||
#include <CGAL/Polygon_mesh_processing/repair.h>
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
|
||||
|
||||
typedef CGAL::Surface_mesh<K::Point_3> Surface_mesh;
|
||||
typedef CGAL::Polyhedron_3<K> Polyhedron;
|
||||
|
||||
typedef std::vector<std::vector<std::size_t> > Vertices_to_merge_container;
|
||||
|
||||
template <typename PolygonMesh>
|
||||
void read_mesh(const char* fname,
|
||||
PolygonMesh& mesh)
|
||||
{
|
||||
std::ifstream input(fname);
|
||||
if (!input || !(input >> mesh) || mesh.is_empty())
|
||||
{
|
||||
std::cerr << fname << " is not a valid off file.\n";
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// tests merge_and_duplication
|
||||
template <typename PolygonMesh>
|
||||
void merge_vertices(typename boost::graph_traits<PolygonMesh>::vertex_descriptor v_keep,
|
||||
typename boost::graph_traits<PolygonMesh>::vertex_descriptor v_rm,
|
||||
PolygonMesh& mesh)
|
||||
{
|
||||
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
|
||||
|
||||
assert(v_keep != v_rm);
|
||||
|
||||
std::size_t ini_nv = static_cast<std::size_t>(vertices(mesh).size());
|
||||
|
||||
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);
|
||||
|
||||
assert(vertices(mesh).size() == ini_nv - 1);
|
||||
}
|
||||
|
||||
template <typename PolygonMesh>
|
||||
void merge_vertices(const Vertices_to_merge_container& all_vertices_to_merge,
|
||||
std::map<typename boost::graph_traits<PolygonMesh>::vertex_descriptor, std::size_t>& merged_onto,
|
||||
PolygonMesh& mesh)
|
||||
{
|
||||
typedef typename boost::graph_traits<PolygonMesh>::vertex_descriptor vertex_descriptor;
|
||||
|
||||
// int to vd
|
||||
std::vector<vertex_descriptor> vds(vertices(mesh).begin(), vertices(mesh).end());
|
||||
|
||||
for(std::size_t i=0, avtmn=all_vertices_to_merge.size(); i<avtmn; ++i)
|
||||
{
|
||||
const std::vector<std::size_t>& vertices_to_merge = all_vertices_to_merge[i];
|
||||
if(vertices_to_merge.size() <= 1)
|
||||
continue;
|
||||
|
||||
vertex_descriptor vd_to_merge_onto = vds[vertices_to_merge[0]];
|
||||
|
||||
for(std::size_t j=1, vtmn=vertices_to_merge.size(); j<vtmn; ++j)
|
||||
merge_vertices(vd_to_merge_onto, vds[vertices_to_merge[j]], mesh);
|
||||
|
||||
std::pair<typename std::map<vertex_descriptor, std::size_t>::iterator, bool > is_insert_successful =
|
||||
merged_onto.insert(std::make_pair(vd_to_merge_onto, vertices_to_merge.size() - 1));
|
||||
if(!is_insert_successful.second)
|
||||
is_insert_successful.first->second += vertices_to_merge.size() - 1;
|
||||
|
||||
assert(CGAL::Polygon_mesh_processing::is_non_manifold_vertex(vd_to_merge_onto, mesh));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename PolygonMesh>
|
||||
std::size_t test_nm_vertices_duplication(const Vertices_to_merge_container& all_merges,
|
||||
std::map<typename boost::graph_traits<PolygonMesh>::vertex_descriptor, std::size_t>& merged_onto,
|
||||
std::vector<std::vector<
|
||||
typename boost::graph_traits<PolygonMesh>::vertex_descriptor> >& duplicated_vertices,
|
||||
PolygonMesh& mesh)
|
||||
{
|
||||
merge_vertices(all_merges, merged_onto, mesh);
|
||||
|
||||
std::size_t new_vertices_nb =
|
||||
CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices(mesh,
|
||||
CGAL::parameters::output_iterator(std::back_inserter(duplicated_vertices)));
|
||||
|
||||
return new_vertices_nb;
|
||||
}
|
||||
|
||||
template <typename PolygonMesh>
|
||||
std::size_t test_nm_vertices_duplication(const Vertices_to_merge_container& all_merges,
|
||||
std::vector<std::vector<
|
||||
typename boost::graph_traits<PolygonMesh>::vertex_descriptor> >& duplicated_vertices,
|
||||
PolygonMesh& mesh)
|
||||
{
|
||||
std::map<typename boost::graph_traits<PolygonMesh>::vertex_descriptor, std::size_t> useless_map;
|
||||
|
||||
return test_nm_vertices_duplication(all_merges, useless_map, duplicated_vertices, mesh);
|
||||
}
|
||||
|
||||
|
||||
template <typename PolygonMesh>
|
||||
void test_unpinched_mesh(const Vertices_to_merge_container& all_merges,
|
||||
PolygonMesh& mesh)
|
||||
{
|
||||
typedef typename boost::graph_traits<PolygonMesh>::vertex_descriptor vertex_descriptor;
|
||||
|
||||
const std::size_t ini_vertices_size = num_vertices(mesh);
|
||||
|
||||
// this is a nice, smooth, surface initially
|
||||
for(vertex_descriptor vd : vertices(mesh)) {
|
||||
assert(!CGAL::Polygon_mesh_processing::is_non_manifold_vertex(vd, mesh));
|
||||
}
|
||||
|
||||
std::vector<std::vector<vertex_descriptor> > duplicated_vertices;
|
||||
std::map<vertex_descriptor, std::size_t> number_of_vertices_merged_onto;
|
||||
std::size_t nb = test_nm_vertices_duplication(all_merges,
|
||||
number_of_vertices_merged_onto,
|
||||
duplicated_vertices,
|
||||
mesh);
|
||||
|
||||
const std::size_t final_vertices_size = vertices(mesh).size();
|
||||
std::cout << " ini: " << ini_vertices_size << " final: " << final_vertices_size << std::endl;
|
||||
assert(final_vertices_size == ini_vertices_size);
|
||||
|
||||
std::size_t expected_nb = 0;
|
||||
for(std::size_t i=0, n=all_merges.size(); i<n; ++i)
|
||||
expected_nb += all_merges[i].size() - 1;
|
||||
assert(nb == expected_nb);
|
||||
|
||||
assert(duplicated_vertices.size() == all_merges.size());
|
||||
for(std::size_t i=0, n=duplicated_vertices.size(); i<n; ++i)
|
||||
assert(duplicated_vertices[i].size() - 1 == number_of_vertices_merged_onto[duplicated_vertices[i][0]]);
|
||||
}
|
||||
|
||||
template <typename PolygonMesh>
|
||||
void test_blobby()
|
||||
{
|
||||
std::cout << " test: data/blobby.off" << std::endl;
|
||||
|
||||
PolygonMesh mesh;
|
||||
read_mesh("data/blobby.off", mesh);
|
||||
|
||||
// non-manifold vertices
|
||||
Vertices_to_merge_container all_merges;
|
||||
std::vector<std::size_t> single_merge;
|
||||
single_merge.push_back(1); single_merge.push_back(7); single_merge.push_back(14); single_merge.push_back(21);
|
||||
all_merges.push_back(single_merge);
|
||||
|
||||
single_merge.clear();
|
||||
single_merge.push_back(2); single_merge.push_back(8);
|
||||
all_merges.push_back(single_merge);
|
||||
|
||||
test_unpinched_mesh(all_merges, mesh);
|
||||
}
|
||||
|
||||
template <typename PolygonMesh>
|
||||
void test_nm_cubes()
|
||||
{
|
||||
std::cout << " test: data_repair/nm_closed_cubes.off" << std::endl;
|
||||
|
||||
PolygonMesh mesh;
|
||||
read_mesh("data_repair/nm_closed_cubes.off", mesh);
|
||||
|
||||
// non-manifold vertices
|
||||
Vertices_to_merge_container all_merges;
|
||||
std::vector<std::size_t> single_merge;
|
||||
single_merge.push_back(5); single_merge.push_back(14);
|
||||
all_merges.push_back(single_merge);
|
||||
|
||||
single_merge.clear();
|
||||
single_merge.push_back(6); single_merge.push_back(15);
|
||||
all_merges.push_back(single_merge);
|
||||
|
||||
test_unpinched_mesh(all_merges, mesh);
|
||||
}
|
||||
|
||||
template <typename PolygonMesh>
|
||||
void test_pinched_triangles(const char* filename,
|
||||
const std::size_t expected_nb)
|
||||
{
|
||||
typedef typename boost::graph_traits<PolygonMesh>::vertex_descriptor vertex_descriptor;
|
||||
|
||||
std::cout << " test: " << filename << " expected: " << expected_nb << std::endl;
|
||||
|
||||
PolygonMesh mesh;
|
||||
read_mesh(filename, mesh);
|
||||
|
||||
// in the triangles, the second (id==1) vertex is non-manifold because it is pinched
|
||||
int id = 0;
|
||||
for(vertex_descriptor vd : vertices(mesh)) {
|
||||
if(id++ == 1) {
|
||||
assert(CGAL::Polygon_mesh_processing::is_non_manifold_vertex(vd, mesh));
|
||||
} else {
|
||||
assert(!CGAL::Polygon_mesh_processing::is_non_manifold_vertex(vd, mesh));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::vector<vertex_descriptor> > duplicated_vertices;
|
||||
std::size_t new_vertices_nb =
|
||||
CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices(mesh,
|
||||
CGAL::parameters::output_iterator(std::back_inserter(duplicated_vertices)));
|
||||
std::cout << " new_vertices_nb: " << new_vertices_nb << " vs expected: " << expected_nb << std::endl;
|
||||
assert(new_vertices_nb == expected_nb);
|
||||
assert(duplicated_vertices.size() == 1);
|
||||
assert(duplicated_vertices[0].size() == 1 + expected_nb);
|
||||
}
|
||||
|
||||
template <typename PolygonMesh>
|
||||
void test_many_umbrellas()
|
||||
{
|
||||
typedef typename boost::graph_traits<PolygonMesh>::vertex_descriptor vertex_descriptor;
|
||||
|
||||
std::cout << " test: data_repair/many_umbrellas.off" << std::endl;
|
||||
|
||||
PolygonMesh mesh;
|
||||
read_mesh("data_repair/many_umbrellas.off", mesh);
|
||||
|
||||
// non-manifold vertices
|
||||
Vertices_to_merge_container all_merges;
|
||||
std::vector<std::size_t> single_merge;
|
||||
single_merge.push_back(1); single_merge.push_back(9); single_merge.push_back(15);
|
||||
all_merges.push_back(single_merge);
|
||||
|
||||
std::vector<std::vector<vertex_descriptor> > duplicated_vertices;
|
||||
std::size_t nb = test_nm_vertices_duplication(all_merges, duplicated_vertices, mesh);
|
||||
assert(nb == 5);
|
||||
|
||||
const std::size_t final_vertices_size = vertices(mesh).size();
|
||||
|
||||
assert(duplicated_vertices.size() == 1);
|
||||
assert(duplicated_vertices[0].size() == 6);
|
||||
assert(final_vertices_size == 19); // 5 new ones, but we merged 2 before, so +3 from '16' at the start
|
||||
}
|
||||
|
||||
int main(int /*argc*/, char** /*argv*/)
|
||||
{
|
||||
std::cout << "Test Vertex Manifoldness Functions (SM)" << std::endl;
|
||||
|
||||
test_blobby<Surface_mesh>(); // data/blobby.off
|
||||
test_nm_cubes<Surface_mesh>(); // data_repair/nm_closed_cubes.off
|
||||
test_pinched_triangles<Surface_mesh>("data_repair/two_triangles_sharing_a_vertex.off", 1);
|
||||
test_pinched_triangles<Surface_mesh>("data_repair/three_triangles_sharing_a_vertex.off", 2);
|
||||
test_many_umbrellas<Surface_mesh>(); // data_repair/many_umbrellas.off
|
||||
|
||||
std::cout << "Test Vertex Manifoldness Functions (Polyhedron)" << std::endl;
|
||||
|
||||
test_blobby<Polyhedron>(); // data/blobby.off
|
||||
test_nm_cubes<Polyhedron>(); // data_repair/nm_closed_cubes.off
|
||||
test_pinched_triangles<Polyhedron>("data_repair/two_triangles_sharing_a_vertex.off", 1);
|
||||
test_pinched_triangles<Polyhedron>("data_repair/three_triangles_sharing_a_vertex.off", 2);
|
||||
test_many_umbrellas<Polyhedron>(); // data_repair/many_umbrellas.off
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
@ -57,109 +57,6 @@ void check_triangle_face_degeneracy(const char* fname)
|
|||
std::cout << "done" << std::endl;
|
||||
}
|
||||
|
||||
// tests merge_and_duplication
|
||||
template <typename PolygonMesh>
|
||||
void merge_identical_points(typename boost::graph_traits<PolygonMesh>::vertex_descriptor v_keep,
|
||||
typename boost::graph_traits<PolygonMesh>::vertex_descriptor v_rm,
|
||||
PolygonMesh& mesh)
|
||||
{
|
||||
typedef typename boost::graph_traits<PolygonMesh>::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 boost::graph_traits<Surface_mesh>::vertex_descriptor vertex_descriptor;
|
||||
typedef boost::graph_traits<Surface_mesh>::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);
|
||||
|
||||
for(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 boost::graph_traits<Surface_mesh>::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<std::vector<vertex_descriptor> > duplicated_vertices;
|
||||
std::size_t new_vertices_nb =
|
||||
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(new_vertices_nb == 4);
|
||||
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...";
|
||||
|
|
@ -243,8 +140,7 @@ 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;
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@
|
|||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
|
||||
#include <boost/type_traits/is_same.hpp>
|
||||
#include <boost/mpl/if.hpp>
|
||||
|
||||
namespace CGAL {
|
||||
|
||||
namespace internal {
|
||||
|
|
@ -131,8 +134,12 @@ struct Dynamic_with_index
|
|||
{
|
||||
typedef Key key_type;
|
||||
typedef Value value_type;
|
||||
typedef value_type& reference;
|
||||
typedef boost::lvalue_property_map_tag category;
|
||||
typedef typename boost::mpl::if_< boost::is_same<bool, Value>,
|
||||
value_type,
|
||||
value_type&>::type reference;
|
||||
typedef typename boost::mpl::if_< boost::is_same<bool, Value>,
|
||||
boost::read_write_property_map_tag,
|
||||
boost::lvalue_property_map_tag>::type category;
|
||||
|
||||
Dynamic_with_index()
|
||||
: m_values()
|
||||
|
|
|
|||
Loading…
Reference in New Issue