diff --git a/Installation/changes.html b/Installation/changes.html index d4e01b057b0..5226528a137 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.
  • 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..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 @@ -28,7 +28,6 @@ #include #include #include -#include #include #include @@ -75,12 +74,12 @@ 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; + typedef typename boost::property_traits::reference Point_ref; VertexPointMap _vpmap; public: @@ -93,126 +92,176 @@ 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); - - 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 + if(normal == typename Traits::Vector_3(0,0,0)) + return false; + std::size_t 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); + halfedge_descriptor v0, v1, v2, v3; + v0 = halfedge(f, pmesh); + Point_ref p0 = _vpmap[target(v0, pmesh)]; + v1 = next(v0, pmesh); + Point_ref p1 = _vpmap[target(v1, pmesh)]; + v2 = next(v1, pmesh); + Point_ref p2 = _vpmap[target(v2, pmesh)]; + v3 = next(v2, pmesh); + Point_ref p3 = _vpmap[target(v3, pmesh)]; - } while( h != start ); - cdt.insert_constraint(previous, first); + /* 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); - // 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) + if(p0p2>p1p3) { - 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)); + } + } + } + + 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); + + 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 + { + // 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( 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)) + { + 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); + + Euler::fill_hole(h0, pmesh); } } } - - // 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); - - 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 - { - // 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( 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)) - { - 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); - - Euler::fill_hole(h0, pmesh); - } - } + return true; } 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()[. @@ -228,8 +277,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) @@ -265,14 +316,16 @@ 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. */ 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) { @@ -288,14 +341,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()); } /** @@ -318,9 +371,11 @@ void 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 -void triangulate_faces(FaceRange face_range, +bool triangulate_faces(FaceRange face_range, PolygonMesh& pmesh, const NamedParameters& np) { @@ -336,13 +391,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()); } /** @@ -360,18 +415,20 @@ void 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 -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 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));