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));