Merge branch 'master' into PMP-Make_remove_self_intersections_local-GF
This commit is contained in:
@@ -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 < \
|
||||
|
||||
+10
-2
@@ -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);
|
||||
|
||||
+5
-1
@@ -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);
|
||||
|
||||
+3
@@ -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; )
|
||||
}
|
||||
|
||||
|
||||
+109
-35
@@ -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
Reference in New Issue
Block a user