From b63dae993a60c9d2a72d0ce528ed838063a8b569 Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Mon, 23 May 2016 15:36:14 +0200 Subject: [PATCH 1/7] Make triangulate_face() a boolean --- .../Polygon_mesh_processing/triangulate_faces.h | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h index 39f50f98f54..d3c6347b92e 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -75,11 +74,10 @@ class Triangulate_modifier P_traits> Fb1; typedef CGAL::Constrained_triangulation_face_base_2 Fb; typedef CGAL::Triangulation_data_structure_2 TDS; - typedef CGAL::No_intersection_tag Itag; + typedef CGAL::Exact_intersections_tag Itag; typedef CGAL::Constrained_Delaunay_triangulation_2 CDTbase; - typedef CGAL::Constrained_triangulation_plus_2 CDT; + Itag> CDT; VertexPointMap _vpmap; @@ -93,11 +91,12 @@ public: return fh->info().is_external; } - void triangulate_face(face_descriptor f, PM& pmesh) + bool triangulate_face(face_descriptor f, PM& pmesh) { typename Traits::Vector_3 normal = Polygon_mesh_processing::compute_face_normal(f, pmesh); - + if(normal == typename Traits::Vector_3(0,0,0)) + return false; P_traits cdt_traits(normal); CDT cdt(cdt_traits); @@ -148,6 +147,11 @@ public: } } } + int original_size = CGAL::halfedges_around_face(halfedge(f, pmesh), pmesh).size(); + + if(cdt.dimension() != 2 || + cdt.number_of_vertices() != original_size) + return false; // then modify the polyhedron // make_hole. (see comment in function body) @@ -208,6 +212,7 @@ public: Euler::fill_hole(h0, pmesh); } } + return true; } template From 0a5bea6dba6e746f67c922d508188748edf73a04 Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Mon, 23 May 2016 16:25:42 +0200 Subject: [PATCH 2/7] Use split_face if the facet is a quad. --- .../triangulate_faces.h | 227 ++++++++++-------- 1 file changed, 129 insertions(+), 98 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h index d3c6347b92e..f9300552819 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h @@ -93,123 +93,154 @@ public: bool triangulate_face(face_descriptor f, PM& pmesh) { + + typename Traits::Vector_3 normal = Polygon_mesh_processing::compute_face_normal(f, pmesh); if(normal == typename Traits::Vector_3(0,0,0)) return false; - P_traits cdt_traits(normal); - CDT cdt(cdt_traits); - - // Halfedge_around_facet_circulator - typedef typename CDT::Vertex_handle Tr_Vertex_handle; - halfedge_descriptor start = halfedge(f, pmesh); - halfedge_descriptor h = start; - Tr_Vertex_handle previous, first; - do + int original_size = CGAL::halfedges_around_face(halfedge(f, pmesh), pmesh).size(); + if(original_size == 4) { - Tr_Vertex_handle vh = cdt.insert(_vpmap[target(h, pmesh)]); - if (first == Tr_Vertex_handle()) { - first = vh; - } - vh->info() = h; - if(previous != Tr_Vertex_handle() && previous != vh) { - cdt.insert_constraint(previous, vh); - } - previous = vh; - h = next(h, pmesh); + typename Kernel::Point_3 p0, p1, p2, p3; + halfedge_descriptor v0, v1, v2, v3; + v0 = halfedge(f, pmesh); + p0 = _vpmap[target(v0, pmesh)]; + v1 = next(v0, pmesh); + p1 = _vpmap[target(v1, pmesh)]; + v2 = next(v1, pmesh); + p2 = _vpmap[target(v2, pmesh)]; + v3 = next(v2, pmesh); + p3 = _vpmap[target(v3, pmesh)]; - } while( h != start ); - cdt.insert_constraint(previous, first); - - // sets mark is_external - for(typename CDT::All_faces_iterator fit = cdt.all_faces_begin(), - end = cdt.all_faces_end(); - fit != end; ++fit) - { - fit->info().is_external = false; - } - std::queue face_queue; - face_queue.push(cdt.infinite_vertex()->face()); - while(! face_queue.empty() ) - { - typename CDT::Face_handle fh = face_queue.front(); - face_queue.pop(); - - if(fh->info().is_external) - continue; - - fh->info().is_external = true; - for(int i = 0; i <3; ++i) + bool predicate = CGAL::cross_product(p2-p1,p0-p1) * CGAL::cross_product(p0-p3,p2-p3) > 0; + if(predicate) { - if(!cdt.is_constrained(typename CDT::Edge(fh, i))) + CGAL::Euler::split_face(v0, v2, pmesh); + } + else + { + CGAL::Euler::split_face(v1, v3, pmesh); + } + + + } + else + { + P_traits cdt_traits(normal); + CDT cdt(cdt_traits); + + // Halfedge_around_facet_circulator + typedef typename CDT::Vertex_handle Tr_Vertex_handle; + halfedge_descriptor start = halfedge(f, pmesh); + halfedge_descriptor h = start; + Tr_Vertex_handle previous, first; + do + { + Tr_Vertex_handle vh = cdt.insert(_vpmap[target(h, pmesh)]); + if (first == Tr_Vertex_handle()) { + first = vh; + } + vh->info() = h; + if(previous != Tr_Vertex_handle() && previous != vh) { + cdt.insert_constraint(previous, vh); + } + previous = vh; + h = next(h, pmesh); + + } while( h != start ); + cdt.insert_constraint(previous, first); + + // sets mark is_external + for(typename CDT::All_faces_iterator fit = cdt.all_faces_begin(), + end = cdt.all_faces_end(); + fit != end; ++fit) + { + fit->info().is_external = false; + } + std::queue face_queue; + face_queue.push(cdt.infinite_vertex()->face()); + while(! face_queue.empty() ) + { + typename CDT::Face_handle fh = face_queue.front(); + face_queue.pop(); + + if(fh->info().is_external) + continue; + + fh->info().is_external = true; + for(int i = 0; i <3; ++i) { - face_queue.push(fh->neighbor(i)); + if(!cdt.is_constrained(typename CDT::Edge(fh, i))) + { + face_queue.push(fh->neighbor(i)); + } } } - } - int original_size = CGAL::halfedges_around_face(halfedge(f, pmesh), pmesh).size(); - if(cdt.dimension() != 2 || - cdt.number_of_vertices() != original_size) - return false; + if(cdt.dimension() != 2 || + cdt.number_of_vertices() != original_size) + return false; - // then modify the polyhedron - // make_hole. (see comment in function body) - this->make_hole(halfedge(f, pmesh), pmesh); - for(typename CDT::Finite_edges_iterator eit = cdt.finite_edges_begin(), - end = cdt.finite_edges_end(); - eit != end; ++eit) - { - typename CDT::Face_handle fh = eit->first; - const int index = eit->second; - typename CDT::Face_handle opposite_fh = fh->neighbor(eit->second); - const int opposite_index = opposite_fh->index(fh); + // then modify the polyhedron + // make_hole. (see comment in function body) + this->make_hole(halfedge(f, pmesh), pmesh); - const Tr_Vertex_handle va = fh->vertex(cdt. cw(index)); - const Tr_Vertex_handle vb = fh->vertex(cdt.ccw(index)); - - if( ! (is_external(fh) && is_external(opposite_fh))//not both fh are external - && ! cdt.is_constrained(*eit) ) //and edge is not constrained + for(typename CDT::Finite_edges_iterator eit = cdt.finite_edges_begin(), + end = cdt.finite_edges_end(); + eit != end; ++eit) { - // strictly internal edge - halfedge_descriptor hnew = halfedge(add_edge(pmesh), pmesh), - hnewopp = opposite(hnew, pmesh); + typename CDT::Face_handle fh = eit->first; + const int index = eit->second; + typename CDT::Face_handle opposite_fh = fh->neighbor(eit->second); + const int opposite_index = opposite_fh->index(fh); - fh->info().e[index] = hnew; - opposite_fh->info().e[opposite_index] = hnewopp; + const Tr_Vertex_handle va = fh->vertex(cdt. cw(index)); + const Tr_Vertex_handle vb = fh->vertex(cdt.ccw(index)); - set_target(hnew, target(va->info(), pmesh), pmesh); - set_target(hnewopp, target(vb->info(), pmesh), pmesh); - } - if( cdt.is_constrained(*eit) ) //edge is constrained - { - if(!is_external(fh)) { - fh->info().e[index] = va->info(); + if( ! (is_external(fh) && is_external(opposite_fh))//not both fh are external + && ! cdt.is_constrained(*eit) ) //and edge is not constrained + { + // strictly internal edge + halfedge_descriptor hnew = halfedge(add_edge(pmesh), pmesh), + hnewopp = opposite(hnew, pmesh); + + fh->info().e[index] = hnew; + opposite_fh->info().e[opposite_index] = hnewopp; + + set_target(hnew, target(va->info(), pmesh), pmesh); + set_target(hnewopp, target(vb->info(), pmesh), pmesh); } - if(!is_external(opposite_fh)) { - opposite_fh->info().e[opposite_index] = vb->info(); + if( cdt.is_constrained(*eit) ) //edge is constrained + { + if(!is_external(fh)) { + fh->info().e[index] = va->info(); + } + if(!is_external(opposite_fh)) { + opposite_fh->info().e[opposite_index] = vb->info(); + } } } - } - for(typename CDT::Finite_faces_iterator fit = cdt.finite_faces_begin(), - end = cdt.finite_faces_end(); - fit != end; ++fit) - { - if(!is_external(fit)) + for(typename CDT::Finite_faces_iterator fit = cdt.finite_faces_begin(), + end = cdt.finite_faces_end(); + fit != end; ++fit) { - halfedge_descriptor h0 = fit->info().e[0]; - halfedge_descriptor h1 = fit->info().e[1]; - halfedge_descriptor h2 = fit->info().e[2]; - CGAL_assertion(h0 != halfedge_descriptor()); - CGAL_assertion(h1 != halfedge_descriptor()); - CGAL_assertion(h2 != halfedge_descriptor()); + if(!is_external(fit)) + { + halfedge_descriptor h0 = fit->info().e[0]; + halfedge_descriptor h1 = fit->info().e[1]; + halfedge_descriptor h2 = fit->info().e[2]; + CGAL_assertion(h0 != halfedge_descriptor()); + CGAL_assertion(h1 != halfedge_descriptor()); + CGAL_assertion(h2 != halfedge_descriptor()); - set_next(h0, h1, pmesh); - set_next(h1, h2, pmesh); - set_next(h2, h0, pmesh); + set_next(h0, h1, pmesh); + set_next(h1, h2, pmesh); + set_next(h2, h0, pmesh); - Euler::fill_hole(h0, pmesh); + Euler::fill_hole(h0, pmesh); + } } } return true; @@ -277,7 +308,7 @@ public: * */ template -void triangulate_face(typename boost::graph_traits::face_descriptor f, +bool triangulate_face(typename boost::graph_traits::face_descriptor f, PolygonMesh& pmesh, const NamedParameters& np) { @@ -293,14 +324,14 @@ void triangulate_face(typename boost::graph_traits::face_descriptor typedef typename GetGeomTraits::type Kernel; internal::Triangulate_modifier modifier(vpmap); - modifier.triangulate_face(f, pmesh); + return modifier.triangulate_face(f, pmesh); } template -void triangulate_face(typename boost::graph_traits::face_descriptor f, +bool triangulate_face(typename boost::graph_traits::face_descriptor f, PolygonMesh& pmesh) { - triangulate_face(f, pmesh, CGAL::Polygon_mesh_processing::parameters::all_default()); + return triangulate_face(f, pmesh, CGAL::Polygon_mesh_processing::parameters::all_default()); } /** From 400f44c2a3fa72b0767e8abddb80f21cabdc9c13 Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Mon, 23 May 2016 16:36:07 +0200 Subject: [PATCH 3/7] Make triangulate_faces() a boolean. --- .../triangulate_faces.h | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h index f9300552819..00359458605 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h @@ -247,8 +247,9 @@ public: } template - void operator()(FaceRange face_range, PM& pmesh) + bool operator()(FaceRange face_range, PM& pmesh) { + bool result = true; // One need to store facet handles into a vector, because the list of // facets of the polyhedron will be modified during the loop, and // that invalidates the range [facets_begin(), facets_end()[. @@ -264,8 +265,10 @@ public: // Iterates on the vector of face descriptors BOOST_FOREACH(face_descriptor f, facets) { - this->triangulate_face(f, pmesh); + if(!this->triangulate_face(f, pmesh)) + result = false; } + return result; } void make_hole(halfedge_descriptor h, PM& pmesh) @@ -356,7 +359,7 @@ bool triangulate_face(typename boost::graph_traits::face_descriptor * */ template -void triangulate_faces(FaceRange face_range, +bool triangulate_faces(FaceRange face_range, PolygonMesh& pmesh, const NamedParameters& np) { @@ -372,13 +375,13 @@ void triangulate_faces(FaceRange face_range, typedef typename GetGeomTraits::type Kernel; internal::Triangulate_modifier modifier(vpmap); - modifier(face_range, pmesh); + return modifier(face_range, pmesh); } template -void triangulate_faces(FaceRange face_range, PolygonMesh& pmesh) +bool triangulate_faces(FaceRange face_range, PolygonMesh& pmesh) { - triangulate_faces(face_range, pmesh, CGAL::Polygon_mesh_processing::parameters::all_default()); + return triangulate_faces(face_range, pmesh, CGAL::Polygon_mesh_processing::parameters::all_default()); } /** @@ -398,16 +401,16 @@ void triangulate_faces(FaceRange face_range, PolygonMesh& pmesh) * */ template -void triangulate_faces(PolygonMesh& pmesh, +bool triangulate_faces(PolygonMesh& pmesh, const NamedParameters& np) { - triangulate_faces(faces(pmesh), pmesh, np); + return triangulate_faces(faces(pmesh), pmesh, np); } template -void triangulate_faces(PolygonMesh& pmesh) +bool triangulate_faces(PolygonMesh& pmesh) { - triangulate_faces(faces(pmesh), pmesh, CGAL::Polygon_mesh_processing::parameters::all_default()); + return triangulate_faces(faces(pmesh), pmesh, CGAL::Polygon_mesh_processing::parameters::all_default()); } } // end namespace Polygon_mesh_processing From d694903fc927862a27591108fb2b07bca85c6f2e Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Mon, 23 May 2016 17:01:27 +0200 Subject: [PATCH 4/7] Adds a warning message in the plugin if all the facets were not triangulated. --- .../demo/Polyhedron/Plugins/PMP/Triangulate_facets_plugin.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Triangulate_facets_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Triangulate_facets_plugin.cpp index 04c101b0d02..a350c95272f 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Triangulate_facets_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Triangulate_facets_plugin.cpp @@ -118,7 +118,8 @@ public Q_SLOTS: QApplication::setOverrideCursor(Qt::WaitCursor); - CGAL::Polygon_mesh_processing::triangulate_faces(*pMesh); + if(!CGAL::Polygon_mesh_processing::triangulate_faces(*pMesh)) + messages->warning(tr("Some facets could not be triangulated.")); CGAL_assertion_code(pMesh->normalize_border()); CGAL_assertion(pMesh->is_valid(false, 3)); From 817155d7f0e3621c20bf520c4c48f2ddb04661af Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Tue, 24 May 2016 08:40:14 +0200 Subject: [PATCH 5/7] Update doc --- .../CGAL/Polygon_mesh_processing/triangulate_faces.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h index 00359458605..9887211c396 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h @@ -99,7 +99,7 @@ public: Polygon_mesh_processing::compute_face_normal(f, pmesh); if(normal == typename Traits::Vector_3(0,0,0)) return false; - int original_size = CGAL::halfedges_around_face(halfedge(f, pmesh), pmesh).size(); + std::size_t original_size = CGAL::halfedges_around_face(halfedge(f, pmesh), pmesh).size(); if(original_size == 4) { typename Kernel::Point_3 p0, p1, p2, p3; @@ -304,11 +304,15 @@ public: * @param pmesh the polygon mesh to which the face to be triangulated belongs * @param np optional sequence of \ref namedparameters among the ones listed below * +* * \cgalNamedParamsBegin * \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh` \cgalParamEnd * \cgalParamBegin{geom_traits} a geometric traits class instance \cgalParamEnd * \cgalNamedParamsEnd * +* @return true if the face has been triangulated. The triangulation can fail if the normal computed for the projection plane is null, +* if the number of points in the triangulation is different from the number of input points +* or if the dimension of the CDT is not 2. */ template bool triangulate_face(typename boost::graph_traits::face_descriptor f, @@ -357,6 +361,8 @@ bool triangulate_face(typename boost::graph_traits::face_descriptor * \cgalParamBegin{geom_traits} a geometric traits class instance \cgalParamEnd * \cgalNamedParamsEnd * +* @return true if all the faces have been triangulated. +* @see triangulate_face() */ template bool triangulate_faces(FaceRange face_range, From 571c87eab618f8fbdb423fd2d72d06bce2ae0665 Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Tue, 24 May 2016 11:04:15 +0200 Subject: [PATCH 6/7] Edit the Changes.html --- Installation/changes.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Installation/changes.html b/Installation/changes.html index 88e972ebd5a..5bdfa32d88b 100644 --- a/Installation/changes.html +++ b/Installation/changes.html @@ -175,6 +175,11 @@ and src/ directories). Add some new features to CGAL::isotropic_remeshing(). It is now possible to select fixed vertices that survive the remeshing process, and to keep face attributes such as colors valid after remeshing. + +
  • + The functions CGAL::Polygon_mesh_processing::triangulate_face() + and CGAL::Polygon_mesh_processing::triangulate_faces() + now indicate whether some faces have not been triangulated.
  • From 1741739449a8337ce62b6ad2c097078493ec8046 Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Wed, 25 May 2016 12:33:43 +0200 Subject: [PATCH 7/7] Fixes the choice of the diagonal for a quad. --- .../triangulate_faces.h | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h index 9887211c396..3bc6cb231dd 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/triangulate_faces.h @@ -79,6 +79,7 @@ class Triangulate_modifier TDS, Itag> CDT; + typedef typename boost::property_traits::reference Point_ref; VertexPointMap _vpmap; public: @@ -102,19 +103,30 @@ public: std::size_t original_size = CGAL::halfedges_around_face(halfedge(f, pmesh), pmesh).size(); if(original_size == 4) { - typename Kernel::Point_3 p0, p1, p2, p3; halfedge_descriptor v0, v1, v2, v3; v0 = halfedge(f, pmesh); - p0 = _vpmap[target(v0, pmesh)]; + Point_ref p0 = _vpmap[target(v0, pmesh)]; v1 = next(v0, pmesh); - p1 = _vpmap[target(v1, pmesh)]; + Point_ref p1 = _vpmap[target(v1, pmesh)]; v2 = next(v1, pmesh); - p2 = _vpmap[target(v2, pmesh)]; + Point_ref p2 = _vpmap[target(v2, pmesh)]; v3 = next(v2, pmesh); - p3 = _vpmap[target(v3, pmesh)]; + Point_ref p3 = _vpmap[target(v3, pmesh)]; - bool predicate = CGAL::cross_product(p2-p1,p0-p1) * CGAL::cross_product(p0-p3,p2-p3) > 0; - if(predicate) + /* Chooses the diagonal that will split the quad in two triangles that maximize + * the scalar product of of the un-normalized normals of the two triangles. + * The lengths of the un-normalized normals (computed using cross-products of two vectors) + * are proportional to the area of the triangles. + * Maximize the scalar product of the two normals will avoid skinny triangles, + * and will also taken into account the cosine of the angle between the two normals. + * In particular, if the two triangles are oriented in different directions, + * the scalar product will be negative. + */ + double p1p3= CGAL::cross_product(p2-p1,p3-p2) * CGAL::cross_product(p0-p3,p1-p0); + double p0p2= CGAL::cross_product(p1-p0,p1-p2) * CGAL::cross_product(p3-p2,p3-p0); + + + if(p0p2>p1p3) { CGAL::Euler::split_face(v0, v2, pmesh); } @@ -310,9 +322,7 @@ public: * \cgalParamBegin{geom_traits} a geometric traits class instance \cgalParamEnd * \cgalNamedParamsEnd * -* @return true if the face has been triangulated. The triangulation can fail if the normal computed for the projection plane is null, -* if the number of points in the triangulation is different from the number of input points -* or if the dimension of the CDT is not 2. +* @return `true` if the face has been triangulated. */ template bool triangulate_face(typename boost::graph_traits::face_descriptor f, @@ -361,7 +371,7 @@ bool triangulate_face(typename boost::graph_traits::face_descriptor * \cgalParamBegin{geom_traits} a geometric traits class instance \cgalParamEnd * \cgalNamedParamsEnd * -* @return true if all the faces have been triangulated. +* @return `true` if all the faces have been triangulated. * @see triangulate_face() */ template @@ -405,6 +415,8 @@ bool triangulate_faces(FaceRange face_range, PolygonMesh& pmesh) * \cgalParamBegin{geom_traits} a geometric traits class instance \cgalParamEnd * \cgalNamedParamsEnd * +* @return `true` if all the faces have been triangulated. +* @see triangulate_face() */ template bool triangulate_faces(PolygonMesh& pmesh,