mirror of https://github.com/CGAL/cgal
Fix edge collapse with incident non-triangular faces (#9117)
Make collapse able to handle non-triangular faces. The fix is easy as you simply don't need to join faces in case the face won't disappear after collapse **TODO:** update doc and constrained version
This commit is contained in:
commit
dee5ed8cc2
|
|
@ -1543,10 +1543,10 @@ does_satisfy_link_condition(typename boost::graph_traits<Graph>::edge_descriptor
|
||||||
*
|
*
|
||||||
* After the collapse of edge `e` the following holds:
|
* After the collapse of edge `e` the following holds:
|
||||||
* - The edge `e` is no longer in `g`.
|
* - The edge `e` is no longer in `g`.
|
||||||
* - The faces incident to edge `e` are no longer in `g`.
|
* - The triangle faces incident to edge `e` are no longer in `g`.
|
||||||
* - `v0` is no longer in `g`.
|
* - `v0` is no longer in `g`.
|
||||||
* - If `h` is not a border halfedge, `p_h` is no longer in `g` and is replaced by `o_n_h`.
|
* - If `h` is part of a triangle face, `p_h` is no longer in `g` and is replaced by `o_n_h`.
|
||||||
* - If the opposite of `h` is not a border halfedge, `p_o_h` is no longer in `g` and is replaced by `o_n_o_h`.
|
* - If the opposite of `h` is part of a triangle face, `p_o_h` is no longer in `g` and is replaced by `o_n_o_h`.
|
||||||
* - The halfedges kept in `g` that had `v0` as target and source now have `v1` as target and source, respectively.
|
* - The halfedges kept in `g` that had `v0` as target and source now have `v1` as target and source, respectively.
|
||||||
* - No other incidence information is changed in `g`.
|
* - No other incidence information is changed in `g`.
|
||||||
*
|
*
|
||||||
|
|
@ -1575,9 +1575,8 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor e,
|
||||||
bool lBottomFaceExists = ! is_border(qp,g);
|
bool lBottomFaceExists = ! is_border(qp,g);
|
||||||
bool lTopLeftFaceExists = lTopFaceExists && ! is_border(pt,g);
|
bool lTopLeftFaceExists = lTopFaceExists && ! is_border(pt,g);
|
||||||
bool lBottomRightFaceExists = lBottomFaceExists && ! is_border(qb,g);
|
bool lBottomRightFaceExists = lBottomFaceExists && ! is_border(qb,g);
|
||||||
|
bool lBottomIsTriangle = lBottomFaceExists && is_triangle(qp,g);
|
||||||
CGAL_precondition( !lTopFaceExists || (lTopFaceExists && ( degree(target(pt, g), g) > 2 ) ) ) ;
|
bool lTopIsTriangle = lTopFaceExists && is_triangle(pq,g);
|
||||||
CGAL_precondition( !lBottomFaceExists || (lBottomFaceExists && ( degree(target(qb, g), g) > 2 ) ) ) ;
|
|
||||||
|
|
||||||
vertex_descriptor q = target(pq, g);
|
vertex_descriptor q = target(pq, g);
|
||||||
vertex_descriptor p = source(pq, g);
|
vertex_descriptor p = source(pq, g);
|
||||||
|
|
@ -1585,7 +1584,7 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor e,
|
||||||
|
|
||||||
bool lP_Erased = false;
|
bool lP_Erased = false;
|
||||||
|
|
||||||
if ( lTopFaceExists )
|
if ( lTopIsTriangle)
|
||||||
{
|
{
|
||||||
CGAL_precondition( ! is_border(opposite(pt, g),g) ) ; // p-q-t is a face of the mesh
|
CGAL_precondition( ! is_border(opposite(pt, g),g) ) ; // p-q-t is a face of the mesh
|
||||||
if ( lTopLeftFaceExists )
|
if ( lTopLeftFaceExists )
|
||||||
|
|
@ -1612,7 +1611,7 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor e,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( lBottomFaceExists )
|
if ( lBottomIsTriangle)
|
||||||
{
|
{
|
||||||
CGAL_precondition( ! is_border(opposite(qb, g),g) ) ; // p-q-b is a face of the mesh
|
CGAL_precondition( ! is_border(opposite(qb, g),g) ) ; // p-q-b is a face of the mesh
|
||||||
if ( lBottomRightFaceExists )
|
if ( lBottomRightFaceExists )
|
||||||
|
|
@ -1659,7 +1658,7 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor e,
|
||||||
* collapses an edge in a graph having non-collapsable edges.
|
* collapses an edge in a graph having non-collapsable edges.
|
||||||
*
|
*
|
||||||
* Let `h` be the halfedge of `e`, and let `v0` and `v1` be the source and target vertices of `h`.
|
* Let `h` be the halfedge of `e`, and let `v0` and `v1` be the source and target vertices of `h`.
|
||||||
* Collapses the edge `e` replacing it with `v1`, as described in the paragraph above
|
* Collapses the edge `e` replacing it with `v1`, as described in the other overload
|
||||||
* and guarantees that an edge `e2`, for which `get(edge_is_constrained_map, e2)==true`,
|
* and guarantees that an edge `e2`, for which `get(edge_is_constrained_map, e2)==true`,
|
||||||
* is not removed after the collapse.
|
* is not removed after the collapse.
|
||||||
*
|
*
|
||||||
|
|
@ -1669,14 +1668,14 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor e,
|
||||||
*
|
*
|
||||||
* \returns vertex `v1`.
|
* \returns vertex `v1`.
|
||||||
* \pre This function requires `g` to be an oriented 2-manifold with or without boundaries.
|
* \pre This function requires `g` to be an oriented 2-manifold with or without boundaries.
|
||||||
* Furthermore, the edge `v0v1` must satisfy the link condition, which guarantees that the surface mesh is also 2-manifold after the edge collapse.
|
* Furthermore, the edge `e` must satisfy the link condition, which guarantees that the surface mesh is also 2-manifold after the edge collapse.
|
||||||
* \pre `get(edge_is_constrained_map, v0v1)==false`.
|
* \pre `get(edge_is_constrained_map, e)==false`.
|
||||||
* \pre `v0` and `v1` are not both incident to a constrained edge.
|
* \pre `v0` and `v1` are not both incident to a constrained edge.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
template<typename Graph, typename EdgeIsConstrainedMap>
|
template<typename Graph, typename EdgeIsConstrainedMap>
|
||||||
typename boost::graph_traits<Graph>::vertex_descriptor
|
typename boost::graph_traits<Graph>::vertex_descriptor
|
||||||
collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
|
collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor e,
|
||||||
Graph& g,
|
Graph& g,
|
||||||
EdgeIsConstrainedMap Edge_is_constrained_map)
|
EdgeIsConstrainedMap Edge_is_constrained_map)
|
||||||
{
|
{
|
||||||
|
|
@ -1684,11 +1683,11 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
|
||||||
typedef typename Traits::vertex_descriptor vertex_descriptor;
|
typedef typename Traits::vertex_descriptor vertex_descriptor;
|
||||||
typedef typename Traits::halfedge_descriptor halfedge_descriptor;
|
typedef typename Traits::halfedge_descriptor halfedge_descriptor;
|
||||||
|
|
||||||
CGAL_precondition(is_valid_edge_descriptor(v0v1, g));
|
CGAL_precondition(is_valid_edge_descriptor(e, g));
|
||||||
CGAL_precondition(does_satisfy_link_condition(v0v1,g));
|
CGAL_precondition(does_satisfy_link_condition(e,g));
|
||||||
CGAL_precondition(!get(Edge_is_constrained_map, v0v1));
|
CGAL_precondition(!get(Edge_is_constrained_map, e));
|
||||||
|
|
||||||
halfedge_descriptor pq = halfedge(v0v1,g);
|
halfedge_descriptor pq = halfedge(e,g);
|
||||||
|
|
||||||
halfedge_descriptor qp = opposite(pq,g);
|
halfedge_descriptor qp = opposite(pq,g);
|
||||||
halfedge_descriptor pt = opposite(prev(pq,g),g);
|
halfedge_descriptor pt = opposite(prev(pq,g),g);
|
||||||
|
|
@ -1698,6 +1697,8 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
|
||||||
|
|
||||||
bool lTopFaceExists = ! is_border(pq,g) ;
|
bool lTopFaceExists = ! is_border(pq,g) ;
|
||||||
bool lBottomFaceExists = ! is_border(qp,g) ;
|
bool lBottomFaceExists = ! is_border(qp,g) ;
|
||||||
|
bool lTopIsTriangle = lTopFaceExists && is_triangle(pq,g);
|
||||||
|
bool lBottomIsTriangle = lBottomFaceExists && is_triangle(qp,g);
|
||||||
|
|
||||||
vertex_descriptor q = target(pq,g);
|
vertex_descriptor q = target(pq,g);
|
||||||
vertex_descriptor p = source(pq,g);
|
vertex_descriptor p = source(pq,g);
|
||||||
|
|
@ -1708,7 +1709,7 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
|
||||||
|
|
||||||
// If the top facet exists, we need to choose one out of the two edges which one disappears:
|
// If the top facet exists, we need to choose one out of the two edges which one disappears:
|
||||||
// p-t if it is not constrained and t-q otherwise
|
// p-t if it is not constrained and t-q otherwise
|
||||||
if ( lTopFaceExists )
|
if ( lTopIsTriangle )
|
||||||
{
|
{
|
||||||
if ( !get(Edge_is_constrained_map,edge(pt,g)) )
|
if ( !get(Edge_is_constrained_map,edge(pt,g)) )
|
||||||
{
|
{
|
||||||
|
|
@ -1722,7 +1723,7 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
|
||||||
|
|
||||||
// If the bottom facet exists, we need to choose one out of the two edges which one disappears:
|
// If the bottom facet exists, we need to choose one out of the two edges which one disappears:
|
||||||
// q-b if it is not constrained and b-p otherwise
|
// q-b if it is not constrained and b-p otherwise
|
||||||
if ( lBottomFaceExists )
|
if ( lBottomIsTriangle )
|
||||||
{
|
{
|
||||||
if ( !get(Edge_is_constrained_map,edge(qb,g)) )
|
if ( !get(Edge_is_constrained_map,edge(qb,g)) )
|
||||||
{
|
{
|
||||||
|
|
@ -1733,7 +1734,7 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lTopFaceExists && lBottomFaceExists)
|
if (lTopIsTriangle && lBottomIsTriangle)
|
||||||
{
|
{
|
||||||
if ( face(edges_to_erase[0],g) == face(edges_to_erase[1],g)
|
if ( face(edges_to_erase[0],g) == face(edges_to_erase[1],g)
|
||||||
&& (! is_border(edges_to_erase[0],g)) )
|
&& (! is_border(edges_to_erase[0],g)) )
|
||||||
|
|
@ -1780,7 +1781,7 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (lTopFaceExists)
|
if (lTopIsTriangle)
|
||||||
{
|
{
|
||||||
if (!(is_border(edges_to_erase[0],g))){
|
if (!(is_border(edges_to_erase[0],g))){
|
||||||
join_face(edges_to_erase[0],g);
|
join_face(edges_to_erase[0],g);
|
||||||
|
|
@ -1795,11 +1796,15 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
|
||||||
remove_face(opposite(edges_to_erase[0],g),g);
|
remove_face(opposite(edges_to_erase[0],g),g);
|
||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (lBottomIsTriangle)
|
||||||
|
{
|
||||||
if (! (is_border(edges_to_erase[0],g))){
|
if (! (is_border(edges_to_erase[0],g))){
|
||||||
// q will be removed, swap it with p
|
// q will be removed, swap it with p
|
||||||
internal::swap_vertices(p, q, g);
|
internal::swap_vertices(p, q, g);
|
||||||
join_face(edges_to_erase[0],g);
|
join_face(edges_to_erase[0],g);
|
||||||
|
CGAL_assertion(source(qp,g)==p);
|
||||||
join_vertex(qp,g);
|
join_vertex(qp,g);
|
||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
|
|
@ -1811,6 +1816,13 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
|
||||||
remove_face(opposite(edges_to_erase[0],g),g);
|
remove_face(opposite(edges_to_erase[0],g),g);
|
||||||
return q;
|
return q;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
join_vertex(pq,g);
|
||||||
|
return q;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// performs an edge flip, rotating the edge pointed by
|
/// performs an edge flip, rotating the edge pointed by
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
OFF
|
||||||
|
25 13 0
|
||||||
|
|
||||||
|
0.39160239696502686 1.3864846229553223 4.8046874923102223e-08
|
||||||
|
0.053782559931278229 1.3864846229553223 4.8046874923102223e-08
|
||||||
|
-0.94644606113433838 1.6651756763458252 4.8046874923102223e-08
|
||||||
|
-1.3082554340362549 1.7385153770446777 4.8046874923102223e-08
|
||||||
|
-1.3033660650253296 1.1860226392745972 4.8046874923102223e-08
|
||||||
|
1.61628258228302 -0.17601536214351654 4.8046874923102223e-08
|
||||||
|
0.55834579467773438 -0.19216139614582062 4.8046874923102223e-08
|
||||||
|
0.053782559931278229 -0.17601536214351654 4.8046874923102223e-08
|
||||||
|
-0.24240998923778534 -0.22679123282432556 4.8046874923102223e-08
|
||||||
|
-0.58168435096740723 -0.25845989584922791 4.8046874923102223e-08
|
||||||
|
-1.2915089130401611 -0.17601536214351654 4.8046874923102223e-08
|
||||||
|
-1.50871741771698 -0.17601536214351654 4.8046874923102223e-08
|
||||||
|
1.61628258228302 -1.7385153770446777 4.8046874923102223e-08
|
||||||
|
1.1978726387023926 -1.7385153770446777 4.8046874923102223e-08
|
||||||
|
0.71942150592803955 -1.7385153770446777 4.8046874923102223e-08
|
||||||
|
0.053782559931278229 -1.7385153770446777 4.8046874923102223e-08
|
||||||
|
-0.73973840475082397 -1.7385153770446777 4.8046874923102223e-08
|
||||||
|
1.61628258228302 0.36264327168464661 4.8046874923102223e-08
|
||||||
|
-0.26156377792358398 0.45463424921035767 4.8046874923102223e-08
|
||||||
|
-0.028661971911787987 -0.78840988874435425 4.8046874923102223e-08
|
||||||
|
0.053782559931278229 -1.2213115692138672 4.8046874923102223e-08
|
||||||
|
-1.5918357372283936 1.5331641435623169 4.8046874923102223e-08
|
||||||
|
-1.6162823438644409 0.87338578701019287 4.8046874923102223e-08
|
||||||
|
-1.50871741771698 -0.0072435899637639523 4.8046874923102223e-08
|
||||||
|
-1.50871741771698 -1.3000825643539429 4.8046874923102223e-08
|
||||||
|
7 18 2 3 4 22 9 8
|
||||||
|
3 2 18 1
|
||||||
|
7 18 7 6 5 17 0 1
|
||||||
|
7 12 5 6 7 8 19 13
|
||||||
|
6 11 24 16 15 20 10
|
||||||
|
3 9 19 8
|
||||||
|
4 10 20 19 9
|
||||||
|
3 7 18 8
|
||||||
|
3 14 20 15
|
||||||
|
4 13 19 20 14
|
||||||
|
3 3 21 4
|
||||||
|
4 9 22 23 10
|
||||||
|
3 10 23 11
|
||||||
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
#include <CGAL/boost/graph/Euler_operations.h>
|
#include <CGAL/boost/graph/Euler_operations.h>
|
||||||
#include <CGAL/boost/graph/IO/OFF.h>
|
#include <CGAL/boost/graph/IO/OFF.h>
|
||||||
|
|
||||||
#include <boost/range/distance.hpp>
|
#include <boost/range/distance.hpp>
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
@ -213,12 +212,30 @@ collapse_edge_test()
|
||||||
assert(found == 2);
|
assert(found == 2);
|
||||||
CGAL::clear(test_mesh);
|
CGAL::clear(test_mesh);
|
||||||
}
|
}
|
||||||
|
// Case 6 non pure triangle mesh
|
||||||
|
{
|
||||||
|
Mesh ref;
|
||||||
|
if(!CGAL::IO::read_OFF("data/polygon_mesh_to_collapse.off", ref))
|
||||||
|
{
|
||||||
|
std::cout << "Error reading file: data/polygon_mesh_to_collapse.off" << std::endl;
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
std::size_t nbe=halfedges(ref).size();
|
||||||
|
for (std::size_t i=0; i< nbe; ++i)
|
||||||
|
{
|
||||||
|
Mesh m = ref;
|
||||||
|
auto h = *std::next(halfedges(m).begin(), i);
|
||||||
|
|
||||||
|
if (CGAL::Euler::does_satisfy_link_condition(edge(h,m),m))
|
||||||
|
CGAL::Euler::collapse_edge(edge(h,m), m);
|
||||||
|
assert(CGAL::is_valid_polygon_mesh(m));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
|
|
||||||
collapse_edge_test<Polyhedron>();
|
collapse_edge_test<Polyhedron>();
|
||||||
collapse_edge_test<SM>();
|
collapse_edge_test<SM>();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue