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:
|
||||
* - 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`.
|
||||
* - If `h` is not a border halfedge, `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 `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 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.
|
||||
* - 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 lTopLeftFaceExists = lTopFaceExists && ! is_border(pt,g);
|
||||
bool lBottomRightFaceExists = lBottomFaceExists && ! is_border(qb,g);
|
||||
|
||||
CGAL_precondition( !lTopFaceExists || (lTopFaceExists && ( degree(target(pt, g), g) > 2 ) ) ) ;
|
||||
CGAL_precondition( !lBottomFaceExists || (lBottomFaceExists && ( degree(target(qb, g), g) > 2 ) ) ) ;
|
||||
bool lBottomIsTriangle = lBottomFaceExists && is_triangle(qp,g);
|
||||
bool lTopIsTriangle = lTopFaceExists && is_triangle(pq,g);
|
||||
|
||||
vertex_descriptor q = target(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;
|
||||
|
||||
if ( lTopFaceExists )
|
||||
if ( lTopIsTriangle)
|
||||
{
|
||||
CGAL_precondition( ! is_border(opposite(pt, g),g) ) ; // p-q-t is a face of the mesh
|
||||
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
|
||||
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.
|
||||
*
|
||||
* 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`,
|
||||
* is not removed after the collapse.
|
||||
*
|
||||
|
|
@ -1669,14 +1668,14 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor e,
|
|||
*
|
||||
* \returns vertex `v1`.
|
||||
* \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.
|
||||
* \pre `get(edge_is_constrained_map, v0v1)==false`.
|
||||
* 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, e)==false`.
|
||||
* \pre `v0` and `v1` are not both incident to a constrained edge.
|
||||
*/
|
||||
|
||||
template<typename Graph, typename EdgeIsConstrainedMap>
|
||||
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,
|
||||
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::halfedge_descriptor halfedge_descriptor;
|
||||
|
||||
CGAL_precondition(is_valid_edge_descriptor(v0v1, g));
|
||||
CGAL_precondition(does_satisfy_link_condition(v0v1,g));
|
||||
CGAL_precondition(!get(Edge_is_constrained_map, v0v1));
|
||||
CGAL_precondition(is_valid_edge_descriptor(e, g));
|
||||
CGAL_precondition(does_satisfy_link_condition(e,g));
|
||||
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 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 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 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:
|
||||
// p-t if it is not constrained and t-q otherwise
|
||||
if ( lTopFaceExists )
|
||||
if ( lTopIsTriangle )
|
||||
{
|
||||
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:
|
||||
// q-b if it is not constrained and b-p otherwise
|
||||
if ( lBottomFaceExists )
|
||||
if ( lBottomIsTriangle )
|
||||
{
|
||||
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)
|
||||
&& (! is_border(edges_to_erase[0],g)) )
|
||||
|
|
@ -1780,7 +1781,7 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
|
|||
}
|
||||
else
|
||||
{
|
||||
if (lTopFaceExists)
|
||||
if (lTopIsTriangle)
|
||||
{
|
||||
if (!(is_border(edges_to_erase[0],g))){
|
||||
join_face(edges_to_erase[0],g);
|
||||
|
|
@ -1795,21 +1796,32 @@ collapse_edge(typename boost::graph_traits<Graph>::edge_descriptor v0v1,
|
|||
remove_face(opposite(edges_to_erase[0],g),g);
|
||||
return q;
|
||||
}
|
||||
|
||||
if (! (is_border(edges_to_erase[0],g))){
|
||||
// q will be removed, swap it with p
|
||||
internal::swap_vertices(p, q, g);
|
||||
join_face(edges_to_erase[0],g);
|
||||
join_vertex(qp,g);
|
||||
return q;
|
||||
}
|
||||
if(!is_border(opposite(next(qp,g),g),g))
|
||||
else
|
||||
{
|
||||
// q will be removed, swap it with p
|
||||
internal::swap_vertices(p, q, g);
|
||||
if (lBottomIsTriangle)
|
||||
{
|
||||
if (! (is_border(edges_to_erase[0],g))){
|
||||
// q will be removed, swap it with p
|
||||
internal::swap_vertices(p, q, g);
|
||||
join_face(edges_to_erase[0],g);
|
||||
CGAL_assertion(source(qp,g)==p);
|
||||
join_vertex(qp,g);
|
||||
return q;
|
||||
}
|
||||
if(!is_border(opposite(next(qp,g),g),g))
|
||||
{
|
||||
// q will be removed, swap it with p
|
||||
internal::swap_vertices(p, q, g);
|
||||
}
|
||||
remove_face(opposite(edges_to_erase[0],g),g);
|
||||
return q;
|
||||
}
|
||||
else
|
||||
{
|
||||
join_vertex(pq,g);
|
||||
return q;
|
||||
}
|
||||
}
|
||||
remove_face(opposite(edges_to_erase[0],g),g);
|
||||
return q;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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/IO/OFF.h>
|
||||
|
||||
#include <boost/range/distance.hpp>
|
||||
|
||||
#include <string>
|
||||
|
|
@ -213,12 +212,30 @@ collapse_edge_test()
|
|||
assert(found == 2);
|
||||
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()
|
||||
{
|
||||
|
||||
collapse_edge_test<Polyhedron>();
|
||||
collapse_edge_test<SM>();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue