fix get_border : return only halfedges that belong to `faces`

This commit is contained in:
Jane Tournois 2015-04-27 10:12:41 +02:00 committed by Sébastien Loriot
parent 8b9bcefe22
commit 940a92d69a
2 changed files with 304 additions and 86 deletions

View File

@ -37,8 +37,6 @@ namespace Polygon_mesh_processing {
* of `faces`, seen from inside the surface patch
*
* @todo code : what shall we do for more than one connected components
* @todo code : make sure the halfedges returned actually belong to a face
* from `faces`. It's not the case yet
*/
template<typename PolygonMesh
, typename FaceRange
@ -49,31 +47,37 @@ namespace Polygon_mesh_processing {
{
typedef PolygonMesh PM;
typedef typename boost::graph_traits<PM>::edge_descriptor edge_descriptor;
typedef typename boost::graph_traits<PM>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<PM>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<PM>::face_descriptor face_descriptor;
//collect halfedges that appear only once
std::set<halfedge_descriptor> border;
// the bool is true if the halfedge stored is the one of the face,
// false if it is its opposite
std::map<halfedge_descriptor, bool> border;
BOOST_FOREACH(face_descriptor f, faces)
{
BOOST_FOREACH(halfedge_descriptor h,
halfedges_around_face(halfedge(f, pmesh), pmesh))
{
//halfedge_descriptor is model of `LessThanComparable`
halfedge_descriptor he = (h < opposite(h, pmesh))
bool from_face = (h < opposite(h, pmesh));
halfedge_descriptor he = from_face
? h
: opposite(h, pmesh);
if (border.find(he) != border.end())
border.erase(he); //even number of appearances
else
border.insert(he);//odd number of appearances
border.insert(std::make_pair(he, from_face));//odd number of appearances
}
}
//copy them in out
BOOST_FOREACH(halfedge_descriptor h, border)
typedef typename std::map<halfedge_descriptor, bool>::value_type HD_bool;
BOOST_FOREACH(const HD_bool& hd, border)
{
*out++ = h;
if (hd.second)
*out++ = hd.first;
else
*out++ = opposite(hd.first, pmesh);
}
}

View File

@ -70,7 +70,6 @@ namespace internal {
, vpmap_(vpmap)
, own_tree_(true)
, input_triangles_()
, patch_(boost::begin(face_range), boost::end(face_range))
, halfedge_status_map_()
{
CGAL_assertion(CGAL::is_triangle_mesh(mesh_));
@ -115,13 +114,15 @@ namespace internal {
Boost_bimap long_edges;
BOOST_FOREACH(edge_descriptor e, edges(mesh_))
{
if (!is_split_or_collapse_allowed(e))
if (!is_split_allowed(e))
continue;
double sqlen = sqlength(e);
if(sqlen > sq_high)
long_edges.insert(long_edge(halfedge(e, mesh_), sqlen));
}
std::cout << "Halfedges : " << nb_valid_halfedges() << std::endl;
//split long edges
unsigned int nb_splits = 0;
while (!long_edges.empty())
@ -136,7 +137,8 @@ namespace internal {
//split edge
halfedge_descriptor hnew = CGAL::Euler::split_edge(he, mesh_);
CGAL_assertion(he == next(hnew, mesh_));
++nb_splits;
//move refinement point
vertex_descriptor vnew = target(hnew, mesh_);
vpmap_[vnew] = refinement_point;
@ -146,59 +148,67 @@ namespace internal {
if (sqlen_new > sq_high)
{
//if it was more than twice the "long" threshold, insert them
long_edges.insert(long_edge(hnew, sqlen_new));
long_edges.insert(long_edge(hnew, sqlen_new));
long_edges.insert(long_edge(next(hnew, mesh_), sqlen_new));
std::cout << "H " << sqlen_new << std::endl;
}
//after splitting
Halfedge_status s1 = status(he);
halfedge_added(hnew, s1);
Halfedge_status s2 = status(opposite(he, mesh_));
halfedge_added(opposite(hnew, mesh_), s2);
CGAL_assertion((s1 == s2 && s1 == PATCH)
|| (s1 != s2 && (s1 == PATCH_BORDER && s2 == MESH_BORDER))
|| (s1 != s2 && (s2 == PATCH_BORDER && s1 == MESH_BORDER))
);
halfedge_added(hnew, status(he));
halfedge_added(opposite(hnew, mesh_), status(opposite(he, mesh_)));
//insert new edges to keep triangular faces, and update long_edges
if (!is_border(hnew, mesh_))
halfedge_descriptor hnew_opp = opposite(hnew, mesh_);
if (!is_on_border(hnew))
{
halfedge_descriptor hnew2 = CGAL::Euler::split_face(hnew,
next(next(hnew, mesh_), mesh_),
mesh_);
halfedge_added(hnew2, PATCH/*only possible scenario*/);
halfedge_added(opposite(hnew2, mesh_), PATCH);
Halfedge_status snew = (is_on_patch(hnew)
|| (is_on_patch_border(hnew) && is_on_mesh(hnew_opp))
|| (is_on_patch_border(hnew) && is_on_border(hnew_opp)))
? PATCH
: MESH;
halfedge_added(hnew2, snew);
halfedge_added(opposite(hnew2, mesh_), snew);
double sql = sqlength(hnew2);
if (sql > sq_high)
if (snew == PATCH)
{
long_edges.insert(long_edge(hnew2, sql));
std::cout << "A " << sql << std::endl;
double sql = sqlength(hnew2);
if (sql > sq_high)
long_edges.insert(long_edge(hnew2, sql));
}
}
//do it again on the other side if we're not on boundary
halfedge_descriptor hnew_opp = opposite(hnew, mesh_);
if (!is_border(hnew_opp, mesh_))
if (!is_on_border(hnew_opp))
{
halfedge_descriptor hnew2 = CGAL::Euler::split_face(prev(hnew_opp, mesh_),
next(hnew_opp, mesh_),
mesh_);
halfedge_added(hnew2, PATCH/*only possible scenario*/);
halfedge_added(opposite(hnew2, mesh_), PATCH);
Halfedge_status snew = (is_on_patch(hnew_opp)
|| (is_on_patch_border(hnew_opp) && is_on_mesh(hnew))
|| (is_on_patch_border(hnew_opp) && is_on_border(hnew)))
? PATCH
: MESH;
halfedge_added(hnew2, snew);
halfedge_added(opposite(hnew2, mesh_), snew);
double sql = sqlength(hnew2);
if (sql > sq_high)
if (snew == PATCH)
{
long_edges.insert(long_edge(hnew2, sql));
std::cout << "B " << sql << std::endl;
double sql = sqlength(hnew2);
if (sql > sq_high)
long_edges.insert(long_edge(hnew2, sql));
}
}
++nb_splits;
CGAL_assertion(halfedge_status_map_.size() == nb_valid_halfedges());
}
std::cout << " done ("<< nb_splits << " splits)." << std::endl;
CGAL_expensive_assertion(is_triangle_mesh(mesh_));
debug_status_map();
debug_patch_border();
debug_mesh_border();
#ifdef CGAL_DUMP_REMESHING_STEPS
dump("1-edge_split.off");
#endif
@ -222,7 +232,7 @@ namespace internal {
Boost_bimap short_edges;
BOOST_FOREACH(edge_descriptor e, edges(mesh_))
{
if (!is_split_or_collapse_allowed(e))
if (!is_collapse_allowed(e))
continue;
double sqlen = sqlength(e);
if (sqlen < sq_low)
@ -238,25 +248,53 @@ namespace internal {
double sqlen = eit->first;
short_edges.right.erase(eit);
//let's try to collapse he into vb
vertex_descriptor va = target(he, mesh_);
vertex_descriptor vb = source(he, mesh_);
//handle the boundary case :
//a boundary edge can be collapsed,
//and an edge incident to boundary can be collapsed,
//but only if the boundary vertex is kept, so re-insert opposite(he)
//to collapse it
//todo : handle this border case using the status map
if (!is_border_edge(he, mesh_))
//a PATCH_BORDER edge can be collapsed,
//and an edge incident to PATCH_BORDER can be collapsed,
//but only if the boundary vertex is kept,
//so re-insert opposite(he) to collapse it
if (is_on_border(he))
{
if (is_border(va, mesh_))
{
if (!is_border(vb, mesh_))
short_edges.insert(short_edge(opposite(he, mesh_), sqlen));
continue;
}
he = opposite(he, mesh_); //he now is PATCH_BORDER
CGAL_assertion(is_on_patch_border(he));
}
else if (is_on_patch_border(opposite(he, mesh_)))
{
CGAL_assertion(is_on_mesh(he));
he = opposite(he, mesh_); //he now is PATCH_BORDER
CGAL_assertion(is_on_patch_border(he));
}
//let's try to collapse he into vb
vertex_descriptor va = source(he, mesh_);
vertex_descriptor vb = target(he, mesh_);
//todo : this test can be restricted to avoid some tests
if (is_on_patch_border(va) && !is_on_patch_border(vb))
{
std::cout << "*";
he = opposite(he, mesh_);
va = source(he, mesh_);
vb = target(he, mesh_);
CGAL_assertion(is_on_patch_border(vb) && !is_on_patch_border(va));
}
else std::cout << "!";
std::vector<std::pair<halfedge_descriptor, Halfedge_status> > vav;
BOOST_FOREACH(halfedge_descriptor ht, halfedges_around_target(va, mesh_))
{
vav.push_back(std::make_pair(ht, status(ht)));
vav.push_back(std::make_pair(opposite(ht, mesh_), status(opposite(ht, mesh_))));
}
std::vector<std::pair<halfedge_descriptor, Halfedge_status> > vbv;
BOOST_FOREACH(halfedge_descriptor ht, halfedges_around_target(vb, mesh_))
{
vbv.push_back(std::make_pair(ht, status(ht)));
vbv.push_back(std::make_pair(opposite(ht, mesh_), status(opposite(ht, mesh_))));
}
CGAL_assertion(is_collapse_allowed(he));
if (degree(va, mesh_) < 3
|| degree(vb, mesh_) < 3
@ -291,13 +329,28 @@ namespace internal {
short_edges.left.erase(opposite(hb, mesh_));
}
CGAL_assertion_code(
halfedge_descriptor en = next(he, mesh_);
halfedge_descriptor enp = next(opposite(he, mesh_), mesh_);
);
//before collapse
unsigned int nb = nb_valid_halfedges();
CGAL_assertion(nb == halfedge_status_map_.size());
Halfedge_status hs = status(he);
Halfedge_status hso = status(opposite(he, mesh_));
std::cout << " status "<< hs << " opp = " << hso << std::endl;
halfedge_descriptor ep_p = prev(opposite(he, mesh_), mesh_);
halfedge_descriptor epo_p = opposite(prev(opposite(he, mesh_), mesh_), mesh_);
Halfedge_status s_ep_p = status( prev(opposite(he, mesh_), mesh_));
Halfedge_status s_epo_p = status(opposite(prev(opposite(he, mesh_), mesh_), mesh_));
CGAL_assertion_code(halfedge_descriptor en = next(he, mesh_));
halfedge_descriptor enp = next(opposite(he, mesh_), mesh_);
halfedge_descriptor enop = opposite(enp, mesh_);
bool mesh_border_case = is_on_border(opposite(he, mesh_));
if (!mesh_border_case)
halfedge_and_opp_removed(prev(opposite(he, mesh_), mesh_));
halfedge_and_opp_removed(he);
halfedge_and_opp_removed(prev(he, mesh_));
//perform collapse
Point target_point = vpmap_[vb];
@ -305,20 +358,45 @@ namespace internal {
vpmap_[vkept] = target_point;
++nb_collapses;
bool kepta = (va == vkept);
bool keptb = (vb == vkept);
if (!mesh_border_case)
{
update_status(enp, s_ep_p);
update_status(enop, s_epo_p);
}
std::vector<std::pair<halfedge_descriptor, Halfedge_status> > vkv;
BOOST_FOREACH(halfedge_descriptor ht, halfedges_around_target(vkept, mesh_))
{
vkv.push_back(std::make_pair(ht, status(ht)));
vkv.push_back(std::make_pair(opposite(ht, mesh_), status(opposite(ht, mesh_))));
}
unsigned int nbb = nb_valid_halfedges();
CGAL_assertion(nbb == halfedge_status_map_.size());
CGAL_assertion(source(en, mesh_) == source(enp, mesh_));
CGAL_expensive_assertion(is_triangle_mesh(mesh_));
debug_status_map();
//insert new/remaining short edges
BOOST_FOREACH(halfedge_descriptor ht, halfedges_around_target(vkept, mesh_))
{
if (!is_collapse_allowed(ht))
continue;
double sqlen = sqlength(ht);
if (sqlen < sq_low)
short_edges.insert(short_edge(ht, sqlen));
}
}
}//end if(collapse_ok)
}
std::cout << " done (" << nb_collapses << " collapses)." << std::endl;
CGAL_assertion(nb_valid_halfedges() == halfedge_status_map_.size());
CGAL_expensive_assertion(is_triangle_mesh(mesh_));
debug_status_map();
debug_patch_border();
debug_mesh_border();
#ifdef CGAL_DUMP_REMESHING_STEPS
dump("2-edge_collapse.off");
#endif
@ -349,9 +427,19 @@ namespace internal {
+ CGAL::abs(valence(vc) - target_valence(vc))
+ CGAL::abs(valence(vd) - target_valence(vd));
Halfedge_status s1 = status(he);
Halfedge_status s1o = status(opposite(he, mesh_));
CGAL::Euler::flip_edge(he, mesh_);
++nb_flips;
Halfedge_status s2 = status(he);
Halfedge_status s2o = status(opposite(he, mesh_));
CGAL_assertion(s1 == s2 && s1 == PATCH);
CGAL_assertion(s1o == s2o && s1o == PATCH);
CGAL_assertion(nb_valid_halfedges() == halfedge_status_map_.size());
CGAL_assertion(!is_border(he, mesh_));
CGAL_assertion(
(vc == target(he, mesh_) && vd == source(he, mesh_))
|| (vd == target(he, mesh_) && vc == source(he, mesh_)));
@ -365,12 +453,18 @@ namespace internal {
{
CGAL::Euler::flip_edge(he, mesh_);
--nb_flips;
Halfedge_status s3 = status(he);
CGAL_assertion(s1 == s3);
CGAL_assertion(!is_border(he, mesh_));
CGAL_assertion(
(va == source(he, mesh_) && vb == target(he, mesh_))
|| (vb == source(he, mesh_) && va == target(he, mesh_)));
}
}
std::cout << "done. ("<< nb_flips << " flips)" << std::endl;
CGAL_assertion(nb_valid_halfedges() == halfedge_status_map_.size());
debug_status_map();
#ifdef CGAL_DUMP_REMESHING_STEPS
dump("3-edge_flips.off");
@ -539,16 +633,28 @@ namespace internal {
&& (plane1.oriented_side(p4) != plane2.oriented_side(p4));
}
bool is_split_or_collapse_allowed(const edge_descriptor& e) const
//todo : simplify the list of cases
bool is_split_allowed(const edge_descriptor& e) const
{
halfedge_descriptor he = halfedge(e, mesh_);
return is_on_patch(he)
return is_on_patch(he) //opp is also on patch
|| (is_on_border(he) && is_on_patch_border(opposite(he, mesh_)))
|| (is_on_border(opposite(he, mesh_)) && is_on_patch_border(he))
|| is_on_patch_border(he)
|| is_on_patch_border(opposite(he, mesh_));
}
//todo : allow to collapse edges on patch boundary (or not?)
bool is_collapse_allowed(const edge_descriptor& e) const
{
halfedge_descriptor he = halfedge(e, mesh_);
return is_on_patch(he) //opp is also on patch
|| (is_on_border(he) && is_on_patch_border(opposite(he, mesh_)))
|| (is_on_border(opposite(he, mesh_)) && is_on_patch_border(he));
}
template<typename FaceRange>
void tag_halfedges_status(FaceRange face_range)
void tag_halfedges_status(const FaceRange& face_range)
{
//tag MESH, //h and hopp belong to the mesh, not the patch
//tag MESH_BORDER //h belongs to the mesh, face(hopp, pmesh) == null_face()
@ -580,23 +686,33 @@ namespace internal {
typename std::map<halfedge_descriptor, Halfedge_status>::iterator it
= halfedge_status_map_.find(h);
CGAL_assertion(it != halfedge_status_map_.end());
CGAL_assertion(it->second == PATCH);
if (it->second == PATCH)
halfedge_status_map_[h] = PATCH_BORDER;
else
{
//todo : this is a workaround because get_border does not always provide
// halfedges from the right side
CGAL_assertion(status(opposite(h, mesh_)) == PATCH);
halfedge_status_map_[opposite(h, mesh_)] = PATCH_BORDER;
}
halfedge_status_map_[h] = PATCH_BORDER;
}
CGAL_assertion(halfedge_status_map_.size() == num_halfedges(mesh_));
CGAL_assertion(halfedge_status_map_.size() == nb_valid_halfedges());
debug_patch_border();
}
Halfedge_status status(const halfedge_descriptor& h) const
{
typename std::map < halfedge_descriptor, Halfedge_status >::const_iterator
it = halfedge_status_map_.find(h);
CGAL_assertion(it != halfedge_status_map_.end());
return it->second;
}
void update_status(const halfedge_descriptor& h, const Halfedge_status& s)
{
CGAL_assertion(halfedge_status_map_.find(h) != halfedge_status_map_.end());
halfedge_status_map_[h] = s;
}
bool is_on_patch(const halfedge_descriptor& h) const
{
return status(h) == PATCH;
bool res =(status(h) == PATCH);
CGAL_assertion(res == (status(opposite(h, mesh_)) == PATCH));
return res;
}
bool is_on_patch(const vertex_descriptor& v) const
@ -612,36 +728,58 @@ namespace internal {
bool is_on_patch_border(const halfedge_descriptor& h) const
{
return status(h) == PATCH_BORDER;
bool res = (status(h) == PATCH_BORDER);
if (res)
{
CGAL_assertion_code(Halfedge_status hs = status(opposite(h, mesh_)));
CGAL_assertion(hs == MESH_BORDER || hs == MESH);
}
return res;
}
bool is_on_patch_border(const edge_descriptor& e) const
{
return is_on_patch_border(halfedge(e,mesh_))
|| is_on_patch_border(opposite(halfedge(e, mesh_), mesh_));
}
bool is_on_patch_border(const vertex_descriptor& v) const
{
BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(v, mesh_))
{
if (is_on_patch_border(h) || is_on_patch_border(opposite(h, mesh_)))
return true;
}
return false;
}
bool is_on_border(const halfedge_descriptor& h) const
{
return status(h) == MESH_BORDER;
Halfedge_status s = status(h);
bool res = (s == MESH_BORDER);
CGAL_assertion(res == is_border(h, mesh_));
CGAL_assertion(res == is_border(next(h, mesh_), mesh_));
CGAL_assertion_code(Halfedge_status s1 = status(opposite(h, mesh_)));
CGAL_assertion_code(Halfedge_status s2 = status(opposite(next(h, mesh_), mesh_)));
if (res && s1 != s2)
CGAL_assertion(is_on_patch_border(target(h, mesh_)));
return res;
}
bool is_on_border(const edge_descriptor& e) const
{
return is_on_border(halfedge(e, mesh_))
|| is_on_border(opposite(halfedge(e, mesh_), mesh_));
}
Halfedge_status status(const halfedge_descriptor& h) const
bool is_on_mesh(const halfedge_descriptor& h) const
{
typename std::map < halfedge_descriptor, Halfedge_status >::const_iterator
it = halfedge_status_map_.find(h);
CGAL_assertion(it != halfedge_status_map_.end());
return it->second;
return status(h) == MESH;
}
void halfedge_added(const halfedge_descriptor& h,
const Halfedge_status& s)
{
halfedge_status_map_[h] = s;
halfedge_status_map_.insert(std::make_pair(h, s));
}
void halfedge_and_opp_removed(const halfedge_descriptor& h)
@ -650,13 +788,89 @@ namespace internal {
halfedge_status_map_.erase(opposite(h, mesh_));
}
unsigned int nb_valid_halfedges() const
{
unsigned int nb = 0;
BOOST_FOREACH(halfedge_descriptor h, halfedges(mesh_))
++nb;
return nb;
}
void debug_status_map() const
{
typedef typename std::map<halfedge_descriptor, Halfedge_status>::value_type
HD_pair;
BOOST_FOREACH(const HD_pair& hs, halfedge_status_map_)
{
bool b1 = is_on_patch(hs.first);
bool b2 = is_on_patch_border(hs.first);
bool b3 = is_on_mesh(hs.first);
bool b4 = is_on_border(hs.first);
//std::cout << hs.first << "\t" << hs.second << "\t ";
//std::cout << b1 << " " << b2 << " " << b3 << " " << b4 << std::endl;
}
}
void debug_patch_border() const
{
std::map<vertex_descriptor, unsigned int> patch_border;
typedef typename std::map<halfedge_descriptor, Halfedge_status>::value_type
HD_pair;
BOOST_FOREACH(const HD_pair& hs, halfedge_status_map_)
{
if (is_on_patch_border(hs.first))
{
if (patch_border.find(target(hs.first, mesh_)) != patch_border.end())
patch_border[target(hs.first, mesh_)]++;
else
patch_border[target(hs.first, mesh_)] = 1;
if (patch_border.find(source(hs.first, mesh_)) != patch_border.end())
patch_border[source(hs.first, mesh_)]++;
else
patch_border[source(hs.first, mesh_)] = 1;
}
}
//check we found each vertex exactly twice
typedef typename std::map<vertex_descriptor, unsigned int>::value_type
V_pair;
BOOST_FOREACH(const V_pair& v, patch_border)
CGAL_assertion(v.second == 2);
}
void debug_mesh_border() const
{
std::map<vertex_descriptor, unsigned int> mesh_border;
typedef typename std::map<halfedge_descriptor, Halfedge_status>::value_type
HD_pair;
BOOST_FOREACH(const HD_pair& hs, halfedge_status_map_)
{
if (is_on_border(hs.first))
{
if (mesh_border.find(target(hs.first, mesh_)) != mesh_border.end())
mesh_border[target(hs.first, mesh_)]++;
else
mesh_border[target(hs.first, mesh_)] = 1;
if (mesh_border.find(source(hs.first, mesh_)) != mesh_border.end())
mesh_border[source(hs.first, mesh_)]++;
else
mesh_border[source(hs.first, mesh_)] = 1;
}
}
//check we found each vertex exactly twice
typedef typename std::map<vertex_descriptor, unsigned int>::value_type
V_pair;
BOOST_FOREACH(const V_pair& v, mesh_border)
CGAL_assertion(v.second == 2);
}
private:
PolygonMesh& mesh_;
VertexPointMap& vpmap_;
const AABB_tree* tree_ptr_;
bool own_tree_;
Triangle_list input_triangles_;
std::vector<face_descriptor> patch_;
std::map<halfedge_descriptor, Halfedge_status> halfedge_status_map_;
};//end class Incremental_remesher