Merge branch 'master' into PMP-Make_remove_self_intersections_local-GF

This commit is contained in:
Mael
2020-03-30 09:44:36 +02:00
committed by GitHub
101 changed files with 5927 additions and 974 deletions
@@ -35,96 +35,6 @@ using Polygon_mesh_processing::Corefinement::Self_intersection_exception;
namespace Polygon_mesh_processing {
namespace internal {
template <class Kernel, class TriangleMesh, class VD, class Fid_map, class Vpm>
bool recursive_does_bound_a_volume(const TriangleMesh& tm,
Vpm& vpm,
Fid_map fid_map,
const std::vector<VD>& xtrm_vertices,
boost::dynamic_bitset<>& cc_handled,
const std::vector<std::size_t>& face_cc,
std::size_t xtrm_cc_id,
bool is_parent_outward_oriented)
{
typedef boost::graph_traits<TriangleMesh> Graph_traits;
typedef typename Graph_traits::face_descriptor face_descriptor;
typedef Side_of_triangle_mesh<TriangleMesh, Kernel, Vpm> Side_of_tm;
// first check that the orientation of the current cc is consistant with its
// parent cc containing it
bool new_is_parent_outward_oriented = internal::is_outward_oriented(
xtrm_vertices[xtrm_cc_id], tm, parameters::vertex_point_map(vpm));
if (new_is_parent_outward_oriented==is_parent_outward_oriented)
return false;
cc_handled.set(xtrm_cc_id);
std::size_t nb_cc = cc_handled.size();
// get all cc that are inside xtrm_cc_id
std::vector<face_descriptor> cc_faces;
for(face_descriptor fd : faces(tm))
{
if(face_cc[get(fid_map, fd)]==xtrm_cc_id)
cc_faces.push_back(fd);
}
typename Side_of_tm::AABB_tree aabb_tree(cc_faces.begin(), cc_faces.end(),
tm, vpm);
Side_of_tm side_of_cc(aabb_tree);
std::vector<std::size_t> cc_inside;
for(std::size_t id=0; id<nb_cc; ++id)
{
if (cc_handled.test(id)) continue;
if (side_of_cc(get(vpm,xtrm_vertices[id]))==ON_BOUNDED_SIDE)
cc_inside.push_back(id);
}
// check whether we need another recursion for cc inside xtrm_cc_id
if (!cc_inside.empty())
{
std::size_t new_xtrm_cc_id = cc_inside.front();
boost::dynamic_bitset<> new_cc_handled(nb_cc,0);
new_cc_handled.set();
new_cc_handled.reset(new_xtrm_cc_id);
cc_handled.set(new_xtrm_cc_id);
std::size_t nb_candidates = cc_inside.size();
for (std::size_t i=1;i<nb_candidates;++i)
{
std::size_t candidate = cc_inside[i];
if(get(vpm,xtrm_vertices[candidate]).z() >
get(vpm,xtrm_vertices[new_xtrm_cc_id]).z()) new_xtrm_cc_id=candidate;
new_cc_handled.reset(candidate);
cc_handled.set(candidate);
}
if ( !internal::recursive_does_bound_a_volume<Kernel>(
tm, vpm, fid_map, xtrm_vertices, new_cc_handled, face_cc,
new_xtrm_cc_id, new_is_parent_outward_oriented) ) return false;
}
// now explore remaining cc included in the same cc as xtrm_cc_id
boost::dynamic_bitset<> cc_not_handled = ~cc_handled;
std::size_t new_xtrm_cc_id = cc_not_handled.find_first();
if (new_xtrm_cc_id == cc_not_handled.npos) return true;
for (std::size_t candidate = cc_not_handled.find_next(new_xtrm_cc_id);
candidate < cc_not_handled.npos;
candidate = cc_not_handled.find_next(candidate))
{
if(get(vpm,xtrm_vertices[candidate]).z() > get(vpm,xtrm_vertices[new_xtrm_cc_id]).z())
new_xtrm_cc_id = candidate;
}
return internal::recursive_does_bound_a_volume<Kernel>(
tm, vpm, fid_map, xtrm_vertices, cc_handled, face_cc,
new_xtrm_cc_id, is_parent_outward_oriented);
}
} //end of namespace internal
namespace Corefinement
{
/** \ingroup PMP_corefinement_grp
@@ -145,99 +55,6 @@ enum Boolean_operation_type {UNION = 0, INTERSECTION=1,
#endif
}
/** \ingroup PMP_corefinement_grp
*
* indicates if `tm` bounds a volume.
* See \ref coref_def_subsec for details.
*
* @tparam TriangleMesh a model of `MutableFaceGraph`, `HalfedgeListGraph` and `FaceListGraph`.
* @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
*
* @param tm a closed triangulated surface mesh
* @param np optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below
*
* @pre `CGAL::is_closed(tm)`
*
* \cgalNamedParamsBegin
* \cgalParamBegin{vertex_point_map}
* the property map with the points associated to the vertices of `tm`.
* If this parameter is omitted, an internal property map for
* `CGAL::vertex_point_t` must be available in `TriangleMesh`
* \cgalParamEnd
* \cgalParamBegin{face_index_map}
* a property map containing the index of each face of `tm`.
* \cgalParamEnd
* \cgalNamedParamsEnd
*
* \see `CGAL::Polygon_mesh_processing::orient_to_bound_a_volume()`
*/
template <class TriangleMesh, class NamedParameters>
bool does_bound_a_volume(const TriangleMesh& tm,
const NamedParameters& np)
{
typedef boost::graph_traits<TriangleMesh> Graph_traits;
typedef typename Graph_traits::vertex_descriptor vertex_descriptor;
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::const_type Vpm;
typedef typename boost::property_traits<Vpm>::value_type Point;
typedef typename Kernel_traits<Point>::Kernel Kernel;
if (!is_closed(tm)) return false;
if (!is_triangle_mesh(tm)) return false;
Vpm vpm = parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point),
get_const_property_map(boost::vertex_point, tm));
typedef typename GetInitializedFaceIndexMap<TriangleMesh, NamedParameters>::const_type Fid_map;
Fid_map fid_map = get_initialized_face_index_map(tm, np);
std::vector<std::size_t> face_cc(num_faces(tm), std::size_t(-1));
// set the connected component id of each face
std::size_t nb_cc = connected_components(tm,
bind_property_maps(fid_map,make_property_map(face_cc)),
parameters::face_index_map(fid_map));
if (nb_cc == 1)
return true;
boost::dynamic_bitset<> cc_handled(nb_cc, 0);
// extract a vertex with max z coordinate for each connected component
std::vector<vertex_descriptor> xtrm_vertices(nb_cc, Graph_traits::null_vertex());
for(vertex_descriptor vd : vertices(tm))
{
std::size_t cc_id = face_cc[get(fid_map, face(halfedge(vd, tm), tm))];
if (xtrm_vertices[cc_id] == Graph_traits::null_vertex())
xtrm_vertices[cc_id]=vd;
else
if (get(vpm, vd).z()>get(vpm,xtrm_vertices[cc_id]).z())
xtrm_vertices[cc_id]=vd;
}
//extract a vertex with max z amongst all components
std::size_t xtrm_cc_id=0;
for(std::size_t id=1; id<nb_cc; ++id)
if (get(vpm, xtrm_vertices[id]).z()>get(vpm,xtrm_vertices[xtrm_cc_id]).z())
xtrm_cc_id=id;
bool is_parent_outward_oriented =
!internal::is_outward_oriented(xtrm_vertices[xtrm_cc_id], tm, np);
return internal::recursive_does_bound_a_volume<Kernel>(tm, vpm, fid_map,
xtrm_vertices,
cc_handled,
face_cc,
xtrm_cc_id,
is_parent_outward_oriented);
}
/// \cond SKIP_IN_MANUAL
template <class TriangleMesh>
bool does_bound_a_volume(const TriangleMesh& tm)
{
return does_bound_a_volume(tm, parameters::all_default());
}
/// \endcond
#define CGAL_COREF_SET_OUTPUT_EDGE_MARK_MAP(I) \
typedef typename internal_np::Lookup_named_param_def < \
@@ -463,11 +463,19 @@ public:
Intersection_edge_map& intersection_edges2 = mesh_to_intersection_edges[&tm2];
// The property map must be either writable or well-initialized
if(!BGL::internal::is_index_map_valid(fids1, num_faces(tm1), faces(tm1)))
if( CGAL::internal::Is_writable_property_map<FaceIdMap1>::value &&
!BGL::internal::is_index_map_valid(fids1, num_faces(tm1), faces(tm1)) )
{
BGL::internal::initialize_face_index_map(fids1, tm1);
}
CGAL_assertion(BGL::internal::is_index_map_valid(fids1, num_faces(tm1), faces(tm1)));
if(!BGL::internal::is_index_map_valid(fids2, num_faces(tm2), faces(tm2)))
if( CGAL::internal::Is_writable_property_map<FaceIdMap2>::value &&
!BGL::internal::is_index_map_valid(fids2, num_faces(tm2), faces(tm2)) )
{
BGL::internal::initialize_face_index_map(fids2, tm2);
}
CGAL_assertion(BGL::internal::is_index_map_valid(fids2, num_faces(tm2), faces(tm2)));
// bitset to identify coplanar faces
boost::dynamic_bitset<> tm1_coplanar_faces(num_faces(tm1), 0);
@@ -237,8 +237,12 @@ public:
}
// The property map must be either writable or well-initialized
if(!BGL::internal::is_index_map_valid(fids, num_faces(tm), faces(tm)))
if( CGAL::internal::Is_writable_property_map<FaceIdMap>::value &&
!BGL::internal::is_index_map_valid(fids, num_faces(tm), faces(tm)) )
{
BGL::internal::initialize_face_index_map(fids, tm);
}
CGAL_assertion(BGL::internal::is_index_map_valid(fids, num_faces(tm), faces(tm)));
// bitset to identify coplanar faces
boost::dynamic_bitset<> tm_coplanar_faces(num_faces(tm), 0);
@@ -1268,6 +1268,8 @@ public:
: nodes(tm1, tm2, vpm1, vpm2)
, visitor(v)
{
CGAL_precondition(is_triangle_mesh(tm1));
CGAL_precondition(is_triangle_mesh(tm2));
CGAL_assertion_code( doing_autorefinement=false; )
}
@@ -1278,6 +1280,7 @@ public:
: nodes(tm, tm, vpm, vpm)
, visitor(v)
{
CGAL_precondition(is_triangle_mesh(tm));
CGAL_assertion_code( doing_autorefinement=true; )
}
@@ -22,8 +22,11 @@
#include <CGAL/Polygon_mesh_processing/internal/named_params_helper.h>
#include <CGAL/Polygon_mesh_processing/stitch_borders.h>
#include <boost/unordered_set.hpp>
#include <boost/bind.hpp>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <vector>
#include <utility>
namespace CGAL {
@@ -58,6 +61,87 @@ struct Less_on_point_of_target
const VertexPointMap& vpm;
};
// Given a container of vectors of halfedges whose target are geometrically indentical,
// check that the intervals described by these pairs are either disjoint or nested.
// This is done to ensure valid combinatorics when we merge the vertices.
// If incompatible (overlapping) intervals are found, the pair representating the longest
// interval (arbitrary choice) is removed from the candidate list.
template <typename VPM, typename PolygonMesh>
void sanitize_candidates(const std::vector<std::pair<typename boost::graph_traits<PolygonMesh>::halfedge_descriptor, std::size_t> >& cycle_hedges,
std::vector<std::vector<std::size_t> >& candidate_hedges_with_id,
const VPM vpm,
const PolygonMesh& pm)
{
if(candidate_hedges_with_id.empty())
return;
std::size_t nm_vertices_n = candidate_hedges_with_id.size();
for(std::size_t fr_id=0, fr_end=nm_vertices_n-1; fr_id<fr_end; ++fr_id)
{
std::vector<std::size_t>& first_candidates = candidate_hedges_with_id[fr_id];
CGAL_assertion(first_candidates.size() >= 2);
for(std::size_t i=0, ie=first_candidates.size()-1; i<ie; ++i)
{
const std::size_t first_left = cycle_hedges[first_candidates[i]].second;
const std::size_t first_right = cycle_hedges[first_candidates[i+1]].second;
CGAL_assertion(first_left < first_right);
for(std::size_t sr_id=i+1, sr_end=nm_vertices_n; sr_id<sr_end; ++sr_id)
{
std::vector<std::size_t>& second_candidates = candidate_hedges_with_id[sr_id];
CGAL_assertion(second_candidates.size() >= 2);
for(std::size_t j=0, je=second_candidates.size()-1; j<je; ++j)
{
const std::size_t second_left = cycle_hedges[second_candidates[j]].second;
const std::size_t second_right = cycle_hedges[second_candidates[j+1]].second;
CGAL_assertion(second_left < second_right);
// The pair of intervals should be either disjoint or nested
// so reject:
// sl -- fl -- sr -- fr and fl -- sl -- fr -- sr
if((second_left < first_left && first_left < second_right && second_right < first_right) ||
(first_left < second_left && second_left < first_right && first_right < second_right))
{
// Remove the candidate with largest range
const std::size_t first_candidates_range =
cycle_hedges[first_candidates.back()].second - cycle_hedges[first_candidates.front()].second;
const std::size_t second_candidates_range =
cycle_hedges[second_candidates.back()].second - cycle_hedges[second_candidates.front()].second;
CGAL_assertion(first_candidates_range <= cycle_hedges.size());
CGAL_assertion(second_candidates_range <= cycle_hedges.size());
#ifdef CGAL_PMP_MERGE_BORDER_VERTICES_DEBUG
std::cout << "Incompatible ranges:\n";
std::cout << "first range: " << first_left << " to " << first_right << std::endl;
std::cout << "second range: " << second_left << " to " << second_right << std::endl;
std::cout << "Full ranges:" << std::endl;
std::cout << cycle_hedges[first_candidates.front()].second << " to " << cycle_hedges[first_candidates.back()].second;
std::cout << " (" << first_candidates.size() << " halfedges)";
std::cout << " at " << get(vpm, target(cycle_hedges[first_candidates.front()].first, pm)) << std::endl;
std::cout << cycle_hedges[second_candidates.front()].second << " to " << cycle_hedges[second_candidates.back()].second;
std::cout << " (" << second_candidates.size() << " halfedges)";
std::cout << " at " << get(vpm, target(cycle_hedges[second_candidates.front()].first, pm)) << std::endl;
#endif
std::vector<std::vector<std::size_t> >::iterator to_remove_iter = candidate_hedges_with_id.begin();
if(first_candidates_range > second_candidates_range)
std::advance(to_remove_iter, fr_id);
else
std::advance(to_remove_iter, sr_id);
candidate_hedges_with_id.erase(to_remove_iter);
// restart the whole thing
return sanitize_candidates(cycle_hedges, candidate_hedges_with_id, vpm, pm);
}
} // entries of the second range
} // second range
} // entries of the first range
} // first range
}
// warning: cycle_hedges will be altered (sorted)
template <class PolygonMesh, class Vpm, class halfedge_descriptor>
@@ -75,26 +159,23 @@ void detect_identical_mergeable_vertices(
std::size_t nbv=cycle_hedges.size();
std::size_t i=1;
std::set< std::pair<std::size_t, std::size_t> > intervals;
// IDs of cycle_hedges
std::vector<std::vector<std::size_t> > candidate_hedges_with_id;
while(i!=nbv)
while(i != nbv)
{
if ( get(vpm, target(cycle_hedges[i].first, pm)) ==
get(vpm, target(cycle_hedges[i-1].first, pm)) )
if(get(vpm, target(cycle_hedges[i].first, pm)) ==
get(vpm, target(cycle_hedges[i-1].first, pm)) )
{
hedges_with_identical_point_target.push_back( std::vector<halfedge_descriptor>() );
hedges_with_identical_point_target.back().push_back(cycle_hedges[i-1].first);
hedges_with_identical_point_target.back().push_back(cycle_hedges[i].first);
intervals.insert( std::make_pair(cycle_hedges[i-1].second, cycle_hedges[i].second) );
std::size_t previous = cycle_hedges[i].second;
while(++i!=nbv)
candidate_hedges_with_id.resize(candidate_hedges_with_id.size() + 1);
candidate_hedges_with_id.back().push_back(i-1);
candidate_hedges_with_id.back().push_back(i);
while(++i != nbv)
{
if ( get(vpm, target(cycle_hedges[i].first, pm)) ==
get(vpm, target(cycle_hedges[i-1].first, pm)) )
if(get(vpm, target(cycle_hedges[i].first, pm)) ==
get(vpm, target(cycle_hedges[i-1].first, pm)))
{
hedges_with_identical_point_target.back().push_back(cycle_hedges[i].first);
intervals.insert( std::make_pair(previous, cycle_hedges[i].second) );
previous = cycle_hedges[i].second;
candidate_hedges_with_id.back().push_back(i);
}
else
{
@@ -104,29 +185,22 @@ void detect_identical_mergeable_vertices(
}
}
else
{
++i;
}
}
// check that intervals are disjoint or strictly nested
// if there is only one issue we drop the whole cycle.
// @todo shall we try to be more conservative?
if (hedges_with_identical_point_target.empty()) return;
std::set< std::pair<std::size_t, std::size_t> >::iterator it1 = intervals.begin(),
end2 = intervals.end(),
end1 = std::prev(end2),
it2;
for (; it1!=end1; ++it1)
for(it2=std::next(it1); it2!= end2; ++it2 )
// Check that intervals are disjoint or strictly nested
sanitize_candidates(cycle_hedges, candidate_hedges_with_id, vpm, pm);
for(const std::vector<std::size_t>& candidates : candidate_hedges_with_id)
{
hedges_with_identical_point_target.resize(hedges_with_identical_point_target.size() + 1);
for(const std::size_t hid : candidates)
{
CGAL_assertion(it1->first<it2->first);
CGAL_assertion(it1->first < it1->second && it2->first < it2->second);
if (it1->second > it2->first && it2->second > it1->second)
{
std::cerr << "Merging is skipt to avoid bad cycle connections\n";
hedges_with_identical_point_target.clear();
return;
}
hedges_with_identical_point_target.back().push_back(cycle_hedges[hid].first);
}
}
}
// \ingroup PMP_repairing_grp
File diff suppressed because it is too large Load Diff