Merge branch 'repair_functions-old' into repair_functions

This commit is contained in:
Mael Rouxel-Labbé 2018-09-17 15:10:39 +02:00
commit 1676cd7405
40 changed files with 2009 additions and 387 deletions

View File

@ -38,16 +38,16 @@ num_edges(const EdgeListGraph& g);
/*! \relates EdgeListGraph
returns the source vertex of `h`.
returns the source vertex of `e`.
*/
template <typename EdgeListGraph>
boost::graph_traits<EdgeListGraph>::vertex_descriptor
source(boost::graph_traits<EdgeListGraph>::halfedge_descriptor h, const EdgeListGraph& g);
source(boost::graph_traits<EdgeListGraph>::edge_descriptor e, const EdgeListGraph& g);
/*! \relates EdgeListGraph
returns the target vertex of `h`.
returns the target vertex of `e`.
*/
template <typename EdgeListGraph>
boost::graph_traits<EdgeListGraph>::vertex_descriptor
target(boost::graph_traits<EdgeListGraph>::halfedge_descriptor h, const EdgeListGraph& g);
target(boost::graph_traits<EdgeListGraph>::edge_descriptor e, const EdgeListGraph& g);

View File

@ -122,12 +122,12 @@ and adds the requirement for traversal of all edges in a graph.
<td>An upper bound of the number of edges of the graph</td>
</tr>
<tr>
<td>`source(g)`</td>
<td>`source(e, g)`</td>
<td>`vertex_descriptor`</td>
<td>The source vertex of `e`</td>
</tr>
<tr>
<td>`target(g)`</td>
<td>`target(e, g)`</td>
<td>`vertex_descriptor`</td>
<td>The target vertex of `e`</td>
</tr>

View File

@ -973,33 +973,6 @@ make_tetrahedron(const P& p0, const P& p1, const P& p2, const P& p3, Graph& g)
return opposite(h2,g);
}
/// \cond SKIP_IN_DOC
template <class Traits, class TriangleMesh, class VertexPointMap>
bool is_degenerate_triangle_face(
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor hd,
TriangleMesh& tmesh,
const VertexPointMap& vpmap,
const Traits& traits)
{
CGAL_assertion(!is_border(hd, tmesh));
const typename Traits::Point_3& p1 = get(vpmap, target( hd, tmesh) );
const typename Traits::Point_3& p2 = get(vpmap, target(next(hd, tmesh), tmesh) );
const typename Traits::Point_3& p3 = get(vpmap, source( hd, tmesh) );
return traits.collinear_3_object()(p1, p2, p3);
}
template <class Traits, class TriangleMesh, class VertexPointMap>
bool is_degenerate_triangle_face(
typename boost::graph_traits<TriangleMesh>::face_descriptor fd,
TriangleMesh& tmesh,
const VertexPointMap& vpmap,
const Traits& traits)
{
return is_degenerate_triangle_face(halfedge(fd,tmesh), tmesh, vpmap, traits);
}
/// \endcond
/**
* \ingroup PkgBGLHelperFct
* \brief Creates a triangulated regular prism, outward oriented,

View File

@ -78,6 +78,7 @@ CGAL_add_named_parameter(projection_functor_t, projection_functor, projection_fu
CGAL_add_named_parameter(throw_on_self_intersection_t, throw_on_self_intersection, throw_on_self_intersection)
CGAL_add_named_parameter(clip_volume_t, clip_volume, clip_volume)
CGAL_add_named_parameter(use_compact_clipper_t, use_compact_clipper, use_compact_clipper)
CGAL_add_named_parameter(output_iterator_t, output_iterator, output_iterator)
// List of named parameters that we use in the package 'Surface Mesh Simplification'
CGAL_add_named_parameter(get_cost_policy_t, get_cost_policy, get_cost)

View File

@ -101,6 +101,7 @@ void test(const NamedParameters& np)
assert(get_param(np, CGAL::internal_np::verbosity_level).v == 41);
assert(get_param(np, CGAL::internal_np::projection_functor).v == 42);
assert(get_param(np, CGAL::internal_np::apply_per_connected_component).v == 46);
assert(get_param(np, CGAL::internal_np::output_iterator).v == 47);
// Test types
@ -177,6 +178,7 @@ void test(const NamedParameters& np)
check_same_type<41>(get_param(np, CGAL::internal_np::verbosity_level));
check_same_type<42>(get_param(np, CGAL::internal_np::projection_functor));
check_same_type<46>(get_param(np, CGAL::internal_np::apply_per_connected_component));
check_same_type<47>(get_param(np, CGAL::internal_np::output_iterator));
}
int main()
@ -238,6 +240,7 @@ int main()
.clip_volume(A<44>(44))
.use_compact_clipper(A<45>(45))
.apply_per_connected_component(A<46>(46))
.output_iterator(A<47>(47))
);
return EXIT_SUCCESS;

View File

@ -1,6 +1,25 @@
Release History
===============
Release 4.14
------------
Release date: March 2019
### Polygon Mesh Processing package
- Added the following new functions to detect and repair mesh degeneracies:
- `CGAL::Polygon_mesh_processing::degenerate_edges()`
- `CGAL::Polygon_mesh_processing::degenerate_faces()`
- `CGAL::Polygon_mesh_processing::is_non_manifold_vertex()`
- `CGAL::Polygon_mesh_processing::is_degenerate_triangle_face()`
- `CGAL::Polygon_mesh_processing::is_degenerate_edge()`
- `CGAL::Polygon_mesh_processing::is_needle_triangle_face()`
- `CGAL::Polygon_mesh_processing::is_cap_triangle_face()`
- `CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices()`
- `CGAL::Polygon_mesh_processing::extract_boundary_cycles()`
- `CGAL::Polygon_mesh_processing::merge_duplicated_vertices_in_boundary_cycle()`
- `CGAL::Polygon_mesh_processing::merge_duplicated_vertices_in_boundary_cycles()`
Release 4.13
------------

View File

@ -722,6 +722,8 @@ public:
\cgalHasModel `CGAL::Vector_2<Kernel>`
\sa `Kernel::ComputeDeterminant_2`
\sa `Kernel::ComputeScalarProduct_2`
\sa `Kernel::ComputeSquaredLength_2`
\sa `Kernel::ComputeX_2`
\sa `Kernel::ComputeY_2`
\sa `Kernel::ComputeHx_2`
@ -754,7 +756,10 @@ A type representing vectors in three dimensions.
\cgalHasModel `CGAL::Vector_3<Kernel>`
\sa `Kernel::CompareDihedralAngle_3`
\sa `Kernel::ComputeDeterminant_3`
\sa `Kernel::ComputeScalarProduct_3`
\sa `Kernel::ComputeSquaredLength_3`
\sa `Kernel::ComputeX_3`
\sa `Kernel::ComputeY_3`
\sa `Kernel::ComputeZ_3`

View File

@ -337,7 +337,7 @@ of a mesh independently.\n
Parameter used to pass a visitor class to a function. Its type and behavior depend on the visited function.
\n
\b Type : `A class` \n
\b Default Specific to the function visited
\b Default : Specific to the function visited
\cgalNPEnd
\cgalNPBegin{throw_on_self_intersection} \anchor PMP_throw_on_self_intersection
@ -364,6 +364,12 @@ should be considered as part of the clipping volume or not.
\b Default value is `true`
\cgalNPEnd
\cgalNPBegin{output_iterator} \anchor PMP_output_iterator
Parameter to pass an output iterator.
\n
\b Type : a model of `OutputIterator` \n
\b Default : `Emptyset_iterator`
\cgalNPEnd
\cgalNPTableEnd

View File

@ -113,19 +113,29 @@ and provides a list of the parameters that are used in this package.
- `CGAL::Polygon_mesh_processing::self_intersections()`
- \link PMP_predicates_grp `CGAL::Polygon_mesh_processing::do_intersect()` \endlink
- `CGAL::Polygon_mesh_processing::intersecting_meshes()`
- `CGAL::Polygon_mesh_processing::is_degenerate_edge()`
- `CGAL::Polygon_mesh_processing::degenerate_edges()`
- `CGAL::Polygon_mesh_processing::is_degenerate_triangle_face()`
- `CGAL::Polygon_mesh_processing::degenerate_faces()`
- `CGAL::Polygon_mesh_processing::is_needle_triangle_face()`
- `CGAL::Polygon_mesh_processing::is_cap_triangle_face()`
## Orientation Functions ##
- `CGAL::Polygon_mesh_processing::is_outward_oriented()`
- `CGAL::Polygon_mesh_processing::reverse_face_orientations()`
- `CGAL::Polygon_mesh_processing::orient_polygon_soup()`
- `CGAL::Polygon_mesh_processing::orient()`
- `CGAL::Polygon_mesh_processing::orient_to_bound_a_volume()`
- `CGAL::Polygon_mesh_processing::is_outward_oriented()`
- `CGAL::Polygon_mesh_processing::reverse_face_orientations()`
## Combinatorial Repairing Functions ##
- \link PMP_repairing_grp `CGAL::Polygon_mesh_processing::stitch_borders()` \endlink
- `CGAL::Polygon_mesh_processing::is_polygon_soup_a_polygon_mesh()`
- `CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh()`
- `CGAL::Polygon_mesh_processing::remove_isolated_vertices()`
- `CGAL::Polygon_mesh_processing::is_non_manifold_vertex()`
- `CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices()`
- `CGAL::Polygon_mesh_processing::merge_duplicated_vertices_in_boundary_cycle()`
- `CGAL::Polygon_mesh_processing::merge_duplicated_vertices_in_boundary_cycles()`
## Normal Computation Functions ##
- `CGAL::Polygon_mesh_processing::compute_face_normal()`
@ -179,6 +189,7 @@ and provides a list of the parameters that are used in this package.
- `CGAL::Polygon_mesh_processing::edge_bbox()`
- `CGAL::Polygon_mesh_processing::face_bbox()`
- `CGAL::Polygon_mesh_processing::border_halfedges()`
- `CGAL::Polygon_mesh_processing::extract_boundary_cycles()`
- `CGAL::Polygon_mesh_processing::transform()`
*/

View File

@ -43,14 +43,14 @@ meshes, refinement, optimization by fairing, and isotropic remeshing of triangul
- \ref Coref_section : methods to corefine triangle meshes and to compute
boolean operations out of corefined closed triangle meshes.
- \ref PMPHoleFilling : available hole filling algorithms, which can possibly be combined with refinement and fairing.
- \ref PMPPredicates : predicates that can be evaluated on the processed polygon
- \ref PMPPredicates : predicates that can be evaluated on the processed polygon.
mesh, which includes point location and self intersection tests.
- \ref PMPOrientation : checking or fixing the \ref PMPOrientation of a polygon soup.
- \ref PMPRepairing : reparation of polygon meshes and polygon soups.
- \ref PMPOrientation : checking or fixing the orientation of a polygon soup.
- \ref PMPRepairing : repair of polygon meshes and polygon soups.
- \ref PMPNormalComp : normal computation at vertices and on faces of a polygon mesh.
- \ref PMPSlicer : functor able to compute the intersections of a polygon mesh with arbitrary planes (slicer).
- \ref PMPConnectedComponents : methods to deal with connected
components of a polygon mesh (extraction, marks, removal, ...)
components of a polygon mesh (extraction, marks, removal, ...).
****************************************
\section PMPMeshing Meshing
@ -335,10 +335,10 @@ From left to right: (a) the hole,
\subsection HoleFillingAPI API
This package provides four functions for hole filling:
- `triangulate_hole_polyline()` : given a sequence of points defining the hole, triangulates the hole.
- `triangulate_hole()` : given a border halfedge on the boundary of the hole on a mesh, triangulates the hole.
- `triangulate_and_refine_hole()` : in addition to `triangulate_hole()` the generated patch is refined.
- `triangulate_refine_and_fair_hole()` : in addition to `triangulate_and_refine_hole()` the generated patch is also faired.
- `triangulate_hole_polyline()` : given a sequence of points defining the hole, triangulates the hole.
- `triangulate_hole()` : given a border halfedge on the boundary of the hole on a mesh, triangulates the hole.
- `triangulate_and_refine_hole()` : in addition to `triangulate_hole()` the generated patch is refined.
- `triangulate_refine_and_fair_hole()` : in addition to `triangulate_and_refine_hole()` the generated patch is also faired.
\subsection HFExamples Examples
@ -372,47 +372,61 @@ iteratively filled, refined and faired to get a faired mesh with no hole.
The hole filling algorithm has a complexity which depends on the
number of vertices. While \cgalCite{liepa2003filling} has a running
time of \f$ O(n^3)\f$ , \cgalCite{zou2013algorithm} in most cases has
running time of \f$ O(n \log n)\f$. We were running
`triangulate_refine_and_fair_hole()` for the below meshes (and two
more meshes with smaller holes). The machine used is a PC running
running time of \f$ O(n \log n)\f$. We benchmarked the function
`triangulate_refine_and_fair_hole()` for the two meshes below (as well as two
more meshes with smaller holes). The machine used was a PC running
Windows 10 with an Intel Core i7 CPU clocked at 2.70 GHz.
The program has been compiled with Visual C++ 2013 compiler with the O2
option which maximizes speed.
The program was compiled with the Visual C++ 2013 compiler with the O2
option, which maximizes speed.
\cgalFigureBegin{Elephants, elephants-with-holes.png}
The elephant on the left/right has a hole with 963/7657 vertices.
\cgalFigureEnd
This takes time
The following running times were observed:
<center>
| # vertices | without Delaunay (sec.) | with Delaunay (sec.)|
| ----: | ----: | ----: |
565 | 8.5 | 0.03 |
774 | 21 | 0.035 |
967 | 43 | 0.06 |
7657 | na | 0.4 |
</center>
***************************************
\section PMPPredicates Predicates
This packages provides several predicates to be evaluated with respect to a triangle mesh.
\subsection PMPSelIntersections Self Intersections
\subsection PMPDoIntersect Intersections Detection
Intersection tests between triangle meshes and/or polylines can be done using
\link PMP_predicates_grp `CGAL::Polygon_mesh_processing::do_intersect()` \endlink.
Additionally, the function `CGAL::Polygon_mesh_processing::intersecting_meshes()`
records all pairs of intersecting meshes in a range.
Self intersections can be detected from a triangle mesh, by calling the predicate
\subsubsection PMPSelIntersections Self Intersections
Self intersections within a triangle mesh can be detected by calling the function
`CGAL::Polygon_mesh_processing::does_self_intersect()`.
Additionally, the function `CGAL::Polygon_mesh_processing::self_intersections()`
reports all pairs of intersecting triangles.
\cgalFigureBegin{SelfIntersections, selfintersections.jpg}
Detecting self-intersections on a triangle mesh.
The intersecting triangles are displayed in dark grey on the right image.
\cgalFigureEnd
\subsubsection SIExample Self Intersections Example
The following example illustrates the detection of self intersection in the `pig.off` mesh.
The detected self-intersection is illustrated on Figure \cgalFigureRef{SelfIntersections}.
\cgalExample{Polygon_mesh_processing/self_intersections_example.cpp}
\cgalFigureAnchor{SelfIntersections}
<center>
<img src="selfintersections.jpg" style="max-width:70%;"/>
</center>
\cgalFigureCaptionBegin{SelfIntersections}
Detecting self-intersections on a triangle mesh.
The intersecting triangles are displayed in dark grey and red on the right image.
\cgalFigureCaptionEnd
\subsection PMPInsideTest Side of Triangle Mesh
@ -435,36 +449,85 @@ input triangle mesh.
\subsubsection InsideExample Inside Test Example
\cgalExample{Polygon_mesh_processing/point_inside_example.cpp}
\subsection PMPDoIntersect Intersections Detection
Intersection tests between triangle meshes and/or polylines can be done using
\link PMP_predicates_grp `CGAL::Polygon_mesh_processing::do_intersect()` \endlink.
Additionally, the function `CGAL::Polygon_mesh_processing::intersecting_meshes()`
records all pairs of intersecting meshes in a range.
\subsection PMPShapePredicates Shape Predicates
Badly shaped or, even worse, completely degenerate elements of a polygon mesh are problematic
in many algorithms which one might want to use on the mesh.
This package offers a toolkit of functions to detect such undesirable elements.
- `CGAL::Polygon_mesh_processing::is_degenerate_edge()`, to detect if an edge is degenerate
(that is, if its two vertices share the same geometric location).
- `CGAL::Polygon_mesh_processing::is_degenerate_triangle_face()`, to detect if a face is
degenerate (that is, if its three vertices are collinear).
- `CGAL::Polygon_mesh_processing::degenerate_edges()`, to collect degenerate edges within a range of edges.
- `CGAL::Polygon_mesh_processing::degenerate_faces()`, to collect degenerate faces within a range of faces.
- `CGAL::Polygon_mesh_processing::is_cap_triangle_face()`
- `CGAL::Polygon_mesh_processing::is_needle_triangle_face()`
****************************************
\section PMPOrientation Orientation
This package offers multiple functions to compute consistent face orientations for set of faces
(Section \ref PolygonSoups) and polygon meshes (Section \ref OrientingPolygonMeshes).
Section \ref PolygonSoupExample offers an example of combination of these functions.
\subsection PolygonSoups Polygon Soups
When the faces of a polygon mesh are given but the connectivity is unknown,
this set of faces is called a \e polygon \e soup.
Before running any of the algorithms on a polygon soup,
one should ensure that the polygons are consistently oriented.
To do so, this package provides the function
`CGAL::Polygon_mesh_processing::orient_polygon_soup()`,
described in \cgalCite{gueziec2001cutting}.
To deal with polygon soups that cannot be converted to a
combinatorially manifold surface, some points must be duplicated.
Because a polygon soup does not have any connectivity (each point
has as many occurences as the number of polygons it belongs to),
duplicating one point (or a pair of points)
amounts to duplicating the polygon to which it belongs.
The duplicated points are either an endpoint of an edge incident to more
than two polygons, an endpoint of an edge between
two polygons with incompatible orientations (during the re-orientation process),
or more generally a point \a p at which the intersection
of an infinitesimally small ball centered at \a p
with the polygons incident to it is not a topological disk.
Once the polygon soup is consistently oriented,
with possibly duplicated (or more) points,
the connectivity can be recovered and made consistent
to build a valid polygon mesh.
The function `CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh()`
performs this mesh construction step.
\subsection OrientingPolygonMeshes Polygon Meshes
This package provides functions dealing with the orientation of faces in a closed polygon mesh.
The function `CGAL::Polygon_mesh_processing::is_outward_oriented()` checks whether
- The function `CGAL::Polygon_mesh_processing::orient()` makes each connected component
of a closed polygon mesh outward- or inward-oriented.
- The function `CGAL::Polygon_mesh_processing::orient_to_bound_a_volume()` orients
the connected components of a closed polygon mesh so that it bounds a volume
(see \ref coref_def_subsec for the precise definition).
- The function `CGAL::Polygon_mesh_processing::is_outward_oriented()` checks whether
an oriented polygon mesh is oriented such that the normals to all faces are oriented towards the
outside of the domain bounded by the input polygon mesh.
The function
`CGAL::Polygon_mesh_processing::reverse_face_orientations()` reverses the orientation
- The function `CGAL::Polygon_mesh_processing::reverse_face_orientations()` reverses the orientation
of halfedges around faces.
As a consequence, the normal computed for each face (see Section
\ref PMPNormalComp) is also reversed.
The \ref PolygonSoupExample puts these functions at work on a polygon soup.
\subsection PolygonSoupExample Orientation Example
The function `CGAL::Polygon_mesh_processing::orient()` makes each connected component
of a closed polygon mesh outward or inward oriented.
The function `CGAL::Polygon_mesh_processing::orient_to_bound_a_volume()` orients
the connected components of a closed polygon mesh so that it bounds a volume
(see \ref coref_def_subsec for the precise definition).
This example shows how to generate a mesh from a polygon soup.
The first step is to get a soup of consistently oriented faces, before
rebuilding the connectivity.
In this example, some orientation tests are performed on the output
polygon mesh to illustrate
Section \ref PMPOrientation.
\cgalExample{Polygon_mesh_processing/orient_polygon_soup_example.cpp}
****************************************
\section PMPRepairing Combinatorial Repairing
@ -490,16 +553,14 @@ with duplicated border edges.
\cgalExample{Polygon_mesh_processing/stitch_borders_example.cpp}
*******************
\if READY_TO_PUBLISH
\subsection DegenerateFaces Removing Degenerate Faces
Some degenerate faces may be part of a given triangle mesh.
A face is considered \e degenerate if two of its vertices
share the same location,
or in general if its three vertices are collinear.
The function
`CGAL::Polygon_mesh_processing::remove_degenerate_faces()`
share the same location, or more generally if its three vertices are collinear.
The function `CGAL::Polygon_mesh_processing::remove_degenerate_faces()`
removes those faces and fixes the connectivity of the newly cleaned up mesh.
It is also possible to remove isolated vertices from any polygon mesh, using the function
`CGAL::Polygon_mesh_processing::remove_isolated_vertices()`.
@ -512,52 +573,35 @@ is output.
\cgalExample{Polygon_mesh_processing/remove_degeneracies_example.cpp}
\endif
*******************
\subsection PolygonSoups Polygon Soups
When the faces of a polygon mesh are given but the connectivity is unknown,
we must deal with of a \e polygon \e soup.
\endcond
Before running any of the algorithms on the so-called
polygon soup, one should ensure that the polygons are consistently oriented.
To do so, this package provides the function
`CGAL::Polygon_mesh_processing::orient_polygon_soup()`,
described in \cgalCite{gueziec2001cutting}.
\subsection PMPManifoldness Polygon Mesh Manifoldness
To deal with polygon soups that cannot be converted to a
combinatorial manifold surface, some points are duplicated.
Because a polygon soup does not have any connectivity (each point
has as many occurrences as the number of polygons it belongs to),
duplicating one point (or a pair of points)
amounts to duplicate the polygon to which it belongs.
Non-manifold vertices can be detected using the function `CGAL::Polygon_mesh_processing::is_non_manifold_vertex()`.
The function `CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices()` can be used
to attempt to create a combinatorially manifold surface mesh by splitting any non-manifold vertex
into as many vertices as there are manifold sheets at this geometric position.
Note however that the mesh will still not be manifold from a geometric
point of view, as the positions of the new vertices introduced at a non-manifold vertex are identical
to the input non-manifold vertex.
The duplicated points are either an endpoint of an edge incident to more
than two polygons, an endpoint of an edge between
two polygons with incompatible orientations (during the re-orientation process),
or more generally a point \a p at which the intersection
of an infinitesimally small ball centered at \a p
with the polygons incident to it is not a topological disk.
\subsubsection FixNMVerticeExample Manifoldness Repair Example
Once the polygon soup is consistently oriented,
with possibly duplicated (or more) points,
the connectivity can be recovered and made consistent
to build a valid polygon mesh.
The function `CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh()`
performs this mesh construction step.
In the following example, a non-manifold configuration is artifically created and
fixed with the help of the functions described above.
\cgalExample{Polygon_mesh_processing/manifoldness_repair_example.cpp}
\subsubsection PolygonSoupExample Polygon Soup Example
This example shows how to generate a mesh from a polygon soup.
The first step is to get a soup of consistently oriented faces, before
rebuilding the connectivity.
In this example, some orientation tests are performed on the output
polygon mesh to illustrate
Section \ref PMPOrientation.
\cgalExample{Polygon_mesh_processing/polygon_soup_example.cpp}
\subsection PMPDuplicateVertexBoundaryCycle Duplicated Vertices in Boundary Cycles
Similarly to the problematic configuration described in the previous section, another issue that can be present
in a polygon mesh is the occurrence of a "pinched" hole, that is the configuration where, when
starting from a border halfedge and walking the halfedges of this border, a geometric position appears
more than once (although, with different vertices) before reaching the initial border halfedge again. The functions
`CGAL::Polygon_mesh_processing::merge_duplicated_vertices_in_boundary_cycle()` and
`CGAL::Polygon_mesh_processing::merge_duplicated_vertices_in_boundary_cycle()`, which merge
vertices at identical positions, can be used to repair this configuration.
****************************************
\section PMPNormalComp Computing Normals
@ -569,7 +613,7 @@ These computations are performed with :
- `CGAL::Polygon_mesh_processing::compute_face_normal()`
- `CGAL::Polygon_mesh_processing::compute_vertex_normal()`
We further provide functions to compute all the normals to faces,
Furthermore, we provide functions to compute all the normals to faces,
or to vertices, or to both :
- `CGAL::Polygon_mesh_processing::compute_face_normals()`
- `CGAL::Polygon_mesh_processing::compute_vertex_normals()`
@ -577,14 +621,12 @@ or to vertices, or to both :
Property maps are used to record the computed normals.
\subsection NormalsExample Normals Computation Examples
Property maps are an API introduced in the boost library, that allows to
Property maps are an API introduced in the boost library that allows to
associate values to keys. In the following examples we associate
a normal vector to each vertex and to each face.
\subsubsection NormalsExampleSM Normals Computation for a Surface Mesh
The following example illustrates how to
@ -728,7 +770,7 @@ that respectively detect the sharp edges, compute the patch indices, and give ea
\subsection DetectFeaturesExample Feature Detection Example
In the following example, we count how many edges of `pmesh` are incident to two faces
which normals form an angle smaller than 90 degrees,
whose normals form an angle smaller than 90 degrees,
and the number of surface patches that are separated by these edges.
\cgalExample{Polygon_mesh_processing/detect_features_example.cpp}

View File

@ -8,7 +8,7 @@
\example Polygon_mesh_processing/triangulate_faces_example.cpp
\example Polygon_mesh_processing/connected_components_example.cpp
\example Polygon_mesh_processing/face_filtered_graph_example.cpp
\example Polygon_mesh_processing/polygon_soup_example.cpp
\example Polygon_mesh_processing/orient_polygon_soup_example.cpp
\example Polygon_mesh_processing/triangulate_polyline_example.cpp
\example Polygon_mesh_processing/refine_fair_example.cpp
\example Polygon_mesh_processing/mesh_slicer_example.cpp
@ -21,4 +21,5 @@
\example Polygon_mesh_processing/corefinement_mesh_union_and_intersection.cpp
\example Polygon_mesh_processing/corefinement_consecutive_bool_op.cpp
\example Polygon_mesh_processing/detect_features_example.cpp
\example Polygon_mesh_processing/manifoldness_repair_example.cpp
*/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 186 KiB

View File

@ -86,7 +86,7 @@ create_single_source_cgal_program( "point_inside_example.cpp")
create_single_source_cgal_program( "triangulate_faces_example.cpp")
create_single_source_cgal_program( "connected_components_example.cpp")
create_single_source_cgal_program( "face_filtered_graph_example.cpp")
create_single_source_cgal_program( "polygon_soup_example.cpp")
create_single_source_cgal_program( "orient_polygon_soup_example.cpp")
create_single_source_cgal_program( "triangulate_polyline_example.cpp")
create_single_source_cgal_program( "mesh_slicer_example.cpp")
#create_single_source_cgal_program( "remove_degeneracies_example.cpp")
@ -104,6 +104,7 @@ create_single_source_cgal_program( "random_perturbation_SM_example.cpp" )
create_single_source_cgal_program( "corefinement_LCC.cpp")
create_single_source_cgal_program( "hole_filling_example_LCC.cpp" )
create_single_source_cgal_program( "detect_features_example.cpp" )
create_single_source_cgal_program( "manifoldness_repair_example.cpp" )
if(OpenMesh_FOUND)
create_single_source_cgal_program( "compute_normals_example_OM.cpp" )

View File

@ -0,0 +1,82 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/boost/graph/iterator.h>
#include <CGAL/Polygon_mesh_processing/repair.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/IO/OFF_reader.h>
#include <boost/foreach.hpp>
#include <fstream>
#include <iostream>
#include <iterator>
#include <vector>
namespace PMP = CGAL::Polygon_mesh_processing;
namespace NP = CGAL::parameters;
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Surface_mesh<K::Point_3> Mesh;
typedef boost::graph_traits<Mesh>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Mesh>::halfedge_descriptor halfedge_descriptor;
void merge_vertices(vertex_descriptor v_keep, vertex_descriptor v_rm, Mesh& mesh)
{
std::cout << "merging vertices " << v_keep << " and " << v_rm << std::endl;
BOOST_FOREACH(halfedge_descriptor h, CGAL::halfedges_around_target(v_rm, mesh)){
set_target(h, v_keep, mesh); // to ensure that no halfedge points at the deleted vertex
}
remove_vertex(v_rm, mesh);
}
int main(int argc, char* argv[])
{
const char* filename = (argc > 1) ? argv[1] : "data/blobby.off";
std::ifstream input(filename);
Mesh mesh;
if(!input || !(input >> mesh) || num_vertices(mesh) == 0)
{
std::cerr << filename << " is not a valid off file.\n";
return EXIT_FAILURE;
}
// Artificially create non-manifoldness for the sake of the example by merging some vertices
vertex_descriptor v0 = *(vertices(mesh).begin());
vertex_descriptor v1 = *(--(vertices(mesh).end()));
merge_vertices(v0, v1, mesh);
// Count non manifold vertices
int counter = 0;
BOOST_FOREACH(vertex_descriptor v, vertices(mesh))
{
if(PMP::is_non_manifold_vertex(v, mesh))
{
std::cout << "vertex " << v << " is non-manifold" << std::endl;
++counter;
}
}
std::cout << counter << " non-manifold occurrence(s)" << std::endl;
// Fix manifoldness by splitting non-manifold vertices
std::vector<std::vector<vertex_descriptor> > duplicated_vertices;
std::size_t new_vertices_nb = PMP::duplicate_non_manifold_vertices(mesh,
NP::output_iterator(
std::back_inserter(duplicated_vertices)));
std::cout << new_vertices_nb << " vertices have been added to fix mesh manifoldness" << std::endl;
for(std::size_t i=0; i<duplicated_vertices.size(); ++i)
{
std::cout << "Non-manifold vertex " << duplicated_vertices[i].front() << " was fixed by creating";
for(std::size_t j=1; j<duplicated_vertices[i].size(); ++j)
std::cout << " " << duplicated_vertices[i][j];
std::cout << std::endl;
}
return EXIT_SUCCESS;
}

View File

@ -1,34 +1,33 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Polyhedron_3.h>
#include <CGAL/IO/OFF_reader.h>
#include <CGAL/Polyhedron_items_with_id_3.h>
#include <CGAL/Polygon_mesh_processing/orient_polygon_soup.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
#include <CGAL/Polygon_mesh_processing/orientation.h>
#include <CGAL/IO/OFF_reader.h>
#include <vector>
#include <fstream>
#include <iostream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Polyhedron_3<K> Polyhedron;
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Polyhedron_3<K, CGAL::Polyhedron_items_with_id_3> Polyhedron;
int main(int argc, char* argv[])
{
const char* filename = (argc > 1) ? argv[1] : "data/tet-shuffled.off";
std::ifstream input(filename);
if (!input)
std::vector<K::Point_3> points;
std::vector<std::vector<std::size_t> > polygons;
if(!input || !CGAL::read_OFF(input, points, polygons) || points.empty())
{
std::cerr << "Cannot open file " << std::endl;
return 1;
}
std::vector<K::Point_3> points;
std::vector< std::vector<std::size_t> > polygons;
if (!CGAL::read_OFF(input, points, polygons))
{
std::cerr << "Error parsing the OFF file " << std::endl;
return 1;
return EXIT_FAILURE;
}
CGAL::Polygon_mesh_processing::orient_polygon_soup(points, polygons);
@ -36,8 +35,13 @@ int main(int argc, char* argv[])
Polyhedron mesh;
CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points, polygons, mesh);
if (CGAL::is_closed(mesh) && (!CGAL::Polygon_mesh_processing::is_outward_oriented(mesh)))
CGAL::Polygon_mesh_processing::reverse_face_orientations(mesh);
// Number the faces because 'orient_to_bound_a_volume' needs a face <--> index map
int index = 0;
for(Polyhedron::Face_iterator fb=mesh.facets_begin(), fe=mesh.facets_end(); fb!=fe; ++fb)
fb->id() = index++;
if(CGAL::is_closed(mesh))
CGAL::Polygon_mesh_processing::orient_to_bound_a_volume(mesh);
std::ofstream out("tet-oriented1.off");
out << mesh;
@ -48,5 +52,5 @@ int main(int argc, char* argv[])
out2 << mesh;
out2.close();
return 0;
return EXIT_SUCCESS;
}

View File

@ -24,16 +24,16 @@
#include <CGAL/license/Polygon_mesh_processing/miscellaneous.h>
#include <CGAL/algorithm.h>
#include <CGAL/boost/graph/iterator.h>
#include <CGAL/Polygon_mesh_processing/internal/named_function_params.h>
#include <CGAL/Polygon_mesh_processing/internal/named_params_helper.h>
#include <boost/graph/graph_traits.hpp>
#include <boost/foreach.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/unordered_set.hpp>
#include <CGAL/Polygon_mesh_processing/internal/named_function_params.h>
#include <CGAL/Polygon_mesh_processing/internal/named_params_helper.h>
#include <CGAL/algorithm.h>
#include <set>
namespace CGAL{
@ -257,6 +257,38 @@ namespace Polygon_mesh_processing {
return border_counter;
}
/// @ingroup PkgPolygonMeshProcessing
/// extracts boundary cycles as a list of halfedges, with one halfedge per border.
///
/// @tparam PolygonMesh a model of `HalfedgeListGraph`
/// @tparam OutputIterator a model of `OutputIterator` holding objects of type
/// `boost::graph_traits<PolygonMesh>::%halfedge_descriptor`
///
/// @param pm a polygon mesh
/// @param out an output iterator where the border halfedges will be put
///
/// @todo It could make sense to also return the length of each cycle.
/// @todo It should probably go into BGL package (like the rest of this file).
template <typename PolygonMesh, typename OutputIterator>
OutputIterator extract_boundary_cycles(PolygonMesh& pm,
OutputIterator out)
{
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
boost::unordered_set<halfedge_descriptor> hedge_handled;
BOOST_FOREACH(halfedge_descriptor h, halfedges(pm))
{
if(is_border(h, pm) && hedge_handled.insert(h).second)
{
*out++ = h;
BOOST_FOREACH(halfedge_descriptor h2, halfedges_around_face(h, pm))
hedge_handled.insert(h2);
}
}
return out;
}
} // end of namespace Polygon_mesh_processing
} // end of namespace CGAL

View File

@ -30,6 +30,7 @@
#include <CGAL/Polygon_mesh_processing/repair.h>
#include <CGAL/Polygon_mesh_processing/measure.h>
#include <CGAL/Polygon_mesh_processing/connected_components.h>
#include <CGAL/Polygon_mesh_processing/shape_predicates.h>
#include <CGAL/AABB_tree.h>
#include <CGAL/AABB_traits.h>
@ -90,21 +91,6 @@ namespace internal {
};
// A property map
template<typename Descriptor>
struct No_constraint_pmap
{
public:
typedef Descriptor key_type;
typedef bool value_type;
typedef value_type& reference;
typedef boost::read_write_property_map_tag category;
friend bool get(const No_constraint_pmap& , const key_type& ) {
return false;
}
friend void put(No_constraint_pmap& , const key_type& , const bool ) {}
};
template <typename PM, typename FaceIndexMap>
struct Border_constraint_pmap
{
@ -325,6 +311,7 @@ namespace internal {
public:
Incremental_remesher(PolygonMesh& pmesh
, VertexPointMap& vpmap
, const GeomTraits& gt
, const bool protect_constraints
, EdgeIsConstrainedMap ecmap
, VertexIsConstrainedMap vcmap
@ -333,6 +320,7 @@ namespace internal {
, const bool build_tree = true)//built by the remesher
: mesh_(pmesh)
, vpmap_(vpmap)
, gt_(gt)
, build_tree_(build_tree)
, has_border_(false)
, input_triangles_()
@ -364,9 +352,10 @@ namespace internal {
BOOST_FOREACH(face_descriptor f, face_range)
{
if (is_degenerate_triangle_face(halfedge(f,mesh_),mesh_,vpmap_,GeomTraits())){
if(is_degenerate_triangle_face(f, mesh_, parameters::vertex_point_map(vpmap_)
.geom_traits(gt_)))
continue;
}
Patch_id pid = get_patch_id(f);
input_triangles_.push_back(triangle(f));
input_patch_ids_.push_back(pid);
@ -828,7 +817,8 @@ namespace internal {
debug_status_map();
debug_self_intersections();
CGAL_assertion(0 == PMP::remove_degenerate_faces(mesh_,
PMP::parameters::vertex_point_map(vpmap_).geom_traits(GeomTraits())));
parameters::vertex_point_map(vpmap_)
.geom_traits(gt_)));
#endif
}
@ -933,7 +923,7 @@ namespace internal {
debug_status_map();
CGAL_assertion(0 == PMP::remove_degenerate_faces(mesh_
, PMP::parameters::vertex_point_map(vpmap_)
.geom_traits(GeomTraits())));
.geom_traits(gt_)));
debug_self_intersections();
#endif
@ -975,9 +965,9 @@ namespace internal {
else if (is_on_patch(v))
{
Vector_3 vn = PMP::compute_vertex_normal(v, mesh_
, PMP::parameters::vertex_point_map(vpmap_)
.geom_traits(GeomTraits()));
Vector_3 vn = PMP::compute_vertex_normal(v, mesh_,
parameters::vertex_point_map(vpmap_)
.geom_traits(gt_));
put(propmap_normals, v, vn);
Vector_3 move = CGAL::NULL_VECTOR;
@ -1469,20 +1459,8 @@ private:
if (f == boost::graph_traits<PM>::null_face())
return CGAL::NULL_VECTOR;
halfedge_descriptor hd = halfedge(f, mesh_);
typename boost::property_traits<VertexPointMap>::reference
p = get(vpmap_, target(hd, mesh_));
hd = next(hd,mesh_);
typename boost::property_traits<VertexPointMap>::reference
q = get(vpmap_, target(hd, mesh_));
hd = next(hd,mesh_);
typename boost::property_traits<VertexPointMap>::reference
r =get(vpmap_, target(hd, mesh_));
if (GeomTraits().collinear_3_object()(p,q,r))
return CGAL::NULL_VECTOR;
else
return PMP::compute_face_normal(f, mesh_, parameters::vertex_point_map(vpmap_));
return PMP::compute_face_normal(f, mesh_, parameters::vertex_point_map(vpmap_)
.geom_traits(gt_));
}
template<typename FaceRange>
@ -1524,7 +1502,7 @@ private:
// update status using constrained edge map
if (!boost::is_same<EdgeIsConstrainedMap,
No_constraint_pmap<edge_descriptor> >::value)
Constant_property_map<edge_descriptor, bool> >::value)
{
BOOST_FOREACH(edge_descriptor e, edges(mesh_))
{
@ -1598,27 +1576,31 @@ private:
const bool collapse_constraints)
{
CGAL_assertion_code(std::size_t nb_done = 0);
boost::unordered_set<halfedge_descriptor> degenerate_faces;
BOOST_FOREACH(halfedge_descriptor h,
halfedges_around_target(halfedge(v, mesh_), mesh_))
{
if (is_border(h, mesh_))
continue;
if (is_degenerate_triangle_face(h, mesh_, vpmap_, GeomTraits()))
if(!is_border(h, mesh_) &&
is_degenerate_triangle_face(face(h, mesh_), mesh_,
parameters::vertex_point_map(vpmap_)
.geom_traits(gt_)))
degenerate_faces.insert(h);
}
while(!degenerate_faces.empty())
{
halfedge_descriptor h = *(degenerate_faces.begin());
degenerate_faces.erase(degenerate_faces.begin());
if (!is_degenerate_triangle_face(h, mesh_, vpmap_, GeomTraits()))
if (!is_degenerate_triangle_face(face(h, mesh_), mesh_,
parameters::vertex_point_map(vpmap_)
.geom_traits(gt_)))
//this can happen when flipping h has consequences further in the mesh
continue;
//check that opposite is not also degenerate
if (degenerate_faces.find(opposite(h, mesh_)) != degenerate_faces.end())
degenerate_faces.erase(opposite(h, mesh_));
degenerate_faces.erase(opposite(h, mesh_));
if(is_border(h, mesh_))
continue;
@ -1664,11 +1646,15 @@ private:
short_edges.insert(typename Bimap::value_type(hf, sqlen));
}
if (!is_border(hf, mesh_)
&& is_degenerate_triangle_face(hf, mesh_, vpmap_, GeomTraits()))
if(!is_border(hf, mesh_) &&
is_degenerate_triangle_face(face(hf, mesh_), mesh_,
parameters::vertex_point_map(vpmap_)
.geom_traits(gt_)))
degenerate_faces.insert(hf);
if (!is_border(hfo, mesh_)
&& is_degenerate_triangle_face(hfo, mesh_, vpmap_, GeomTraits()))
if(!is_border(hfo, mesh_) &&
is_degenerate_triangle_face(face(hfo, mesh_), mesh_,
parameters::vertex_point_map(vpmap_)
.geom_traits(gt_)))
degenerate_faces.insert(hfo);
break;
@ -1685,9 +1671,10 @@ private:
BOOST_FOREACH(halfedge_descriptor h,
halfedges_around_target(he, mesh_))
{
if (is_border(h, mesh_))
continue;
if (is_degenerate_triangle_face(h, mesh_, vpmap_, GeomTraits()))
if(!is_border(h, mesh_) &&
is_degenerate_triangle_face(face(h, mesh_), mesh_,
parameters::vertex_point_map(vpmap_)
.geom_traits(gt_)))
return true;
}
return false;
@ -1828,10 +1815,10 @@ private:
{
std::cout << "Test self intersections...";
std::vector<std::pair<face_descriptor, face_descriptor> > facets;
PMP::self_intersections(
mesh_,
std::back_inserter(facets),
PMP::parameters::vertex_point_map(vpmap_));
PMP::self_intersections(mesh_,
std::back_inserter(facets),
PMP::parameters::vertex_point_map(vpmap_)
.geom_traits(gt_));
//CGAL_assertion(facets.empty());
std::cout << "done ("<< facets.size() <<" facets)." << std::endl;
}
@ -1840,11 +1827,11 @@ private:
{
std::cout << "Test self intersections...";
std::vector<std::pair<face_descriptor, face_descriptor> > facets;
PMP::self_intersections(
faces_around_target(halfedge(v, mesh_), mesh_),
mesh_,
std::back_inserter(facets),
PMP::parameters::vertex_point_map(vpmap_));
PMP::self_intersections(faces_around_target(halfedge(v, mesh_), mesh_),
mesh_,
std::back_inserter(facets),
PMP::parameters::vertex_point_map(vpmap_)
.geom_traits(gt_));
//CGAL_assertion(facets.empty());
std::cout << "done ("<< facets.size() <<" facets)." << std::endl;
}
@ -1926,6 +1913,7 @@ private:
private:
PolygonMesh& mesh_;
VertexPointMap& vpmap_;
const GeomTraits& gt_;
bool build_tree_;
bool has_border_;
std::vector<AABB_tree*> trees;

View File

@ -0,0 +1,293 @@
// Copyright (c) 2018 GeometryFactory (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
// You can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0+
//
//
// Author(s) : Sebastien Loriot
#ifndef CGAL_POLYGON_MESH_PROCESSING_MERGE_BORDER_VERTICES_H
#define CGAL_POLYGON_MESH_PROCESSING_MERGE_BORDER_VERTICES_H
#include <CGAL/license/Polygon_mesh_processing/repair.h>
#include <CGAL/boost/graph/helpers.h>
#include <CGAL/boost/graph/properties.h>
#include <CGAL/Polygon_mesh_processing/border.h>
#include <CGAL/Polygon_mesh_processing/internal/named_function_params.h>
#include <CGAL/Polygon_mesh_processing/internal/named_params_helper.h>
#include <CGAL/Polygon_mesh_processing/stitch_borders.h>
#include <boost/unordered_set.hpp>
#include <boost/bind.hpp>
namespace CGAL {
namespace Polygon_mesh_processing {
namespace internal {
template <typename PM, typename VertexPointMap>
struct Less_on_point_of_target
{
typedef typename boost::graph_traits<PM>::halfedge_descriptor
halfedge_descriptor;
typedef typename boost::property_traits<VertexPointMap>::reference Point;
Less_on_point_of_target(const PM& pm,
const VertexPointMap& vpm)
: pm(pm),
vpm(vpm)
{}
bool operator()(const std::pair<halfedge_descriptor, std::size_t>& h1,
const std::pair<halfedge_descriptor, std::size_t>& h2) const
{
if ( get(vpm, target(h1.first, pm)) < get(vpm, target(h2.first, pm)) )
return true;
if ( get(vpm, target(h1.first, pm)) > get(vpm, target(h2.first, pm)) )
return false;
return h1.second < h2.second;
}
const PM& pm;
const VertexPointMap& vpm;
};
// warning: cycle_hedges will be altered (sorted)
template <class PolygonMesh, class Vpm, class halfedge_descriptor>
void detect_identical_mergeable_vertices(
std::vector< std::pair<halfedge_descriptor, std::size_t> >& cycle_hedges,
std::vector< std::vector<halfedge_descriptor> >& hedges_with_identical_point_target,
const PolygonMesh& pm,
Vpm vpm)
{
// sort vertices using their point to ease the detection
// of vertices with identical points
Less_on_point_of_target<PolygonMesh, Vpm> less(pm, vpm);
std::sort( cycle_hedges.begin(), cycle_hedges.end(), less);
std::size_t nbv=cycle_hedges.size();
std::size_t i=1;
std::set< std::pair<std::size_t, std::size_t> > intervals;
while(i!=nbv)
{
if ( get(vpm, target(cycle_hedges[i].first, pm)) ==
get(vpm, target(cycle_hedges[i-1].first, pm)) )
{
hedges_with_identical_point_target.push_back( std::vector<halfedge_descriptor>() );
hedges_with_identical_point_target.back().push_back(cycle_hedges[i-1].first);
hedges_with_identical_point_target.back().push_back(cycle_hedges[i].first);
intervals.insert( std::make_pair(cycle_hedges[i-1].second, cycle_hedges[i].second) );
std::size_t previous = cycle_hedges[i].second;
while(++i!=nbv)
{
if ( get(vpm, target(cycle_hedges[i].first, pm)) ==
get(vpm, target(cycle_hedges[i-1].first, pm)) )
{
hedges_with_identical_point_target.back().push_back(cycle_hedges[i].first);
intervals.insert( std::make_pair(previous, cycle_hedges[i].second) );
previous = cycle_hedges[i].second;
}
else
{
++i;
break;
}
}
}
else
++i;
}
// check that intervals are disjoint or strictly nested
// if there is only one issue we drop the whole cycle.
/// \todo shall we try to be more conservative?
if (hedges_with_identical_point_target.empty()) return;
std::set< std::pair<std::size_t, std::size_t> >::iterator it1 = intervals.begin(),
end2 = intervals.end(),
end1 = cpp11::prev(end2),
it2;
for (; it1!=end1; ++it1)
for(it2=cpp11::next(it1); it2!= end2; ++it2 )
{
CGAL_assertion(it1->first<it2->first);
CGAL_assertion(it1->first < it1->second && it2->first < it2->second);
if (it1->second > it2->first && it2->second > it1->second)
{
std::cerr << "Merging is skipt to avoid bad cycle connections\n";
hedges_with_identical_point_target.clear();
return;
}
}
}
// \ingroup PMP_repairing_grp
// merges target vertices of a list of halfedges.
// Halfedges must be sorted in the list.
//
// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph`.
// @tparam HalfedgeRange a range of halfedge descriptors of `PolygonMesh`, model of `Range`.
//
// @param sorted_hedges a sorted list of halfedges.
// @param pm the polygon mesh which contains the list of halfedges.
//
template <typename PolygonMesh, class HalfedgeRange>
void merge_vertices_in_range(const HalfedgeRange& sorted_hedges,
PolygonMesh& pm)
{
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<PolygonMesh>::vertex_descriptor vertex_descriptor;
halfedge_descriptor in_h_kept = *boost::begin(sorted_hedges);
halfedge_descriptor out_h_kept = next(in_h_kept, pm);
vertex_descriptor v_kept = target(in_h_kept, pm);
std::vector<vertex_descriptor> vertices_to_rm;
BOOST_FOREACH(halfedge_descriptor in_h_rm, sorted_hedges)
{
vertex_descriptor vd = target(in_h_rm, pm);
if (vd==v_kept) continue; // skip identical vertices (in particular this skips the first halfedge)
if (edge(vd, v_kept, pm).second) continue; // skip null edges
bool shall_continue=false;
BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(v_kept, pm))
{
if (edge(vd, source(h, pm), pm).second)
{
shall_continue=true;
break;
}
}
if (shall_continue) continue; // skip vertices already incident to the same vertex
// update the vertex of the halfedges incident to the vertex to remove
internal::update_target_vertex(in_h_rm, v_kept, pm);
// update next/prev pointers around the 2 vertices to be merged
halfedge_descriptor out_h_rm = next(in_h_rm, pm);
set_next(in_h_kept, out_h_rm, pm);
set_next(in_h_rm, out_h_kept, pm);
vertices_to_rm.push_back(vd);
out_h_kept=out_h_rm;
}
BOOST_FOREACH(vertex_descriptor vd, vertices_to_rm)
remove_vertex(vd, pm);
}
} // end of internal
/// \ingroup PMP_repairing_grp
/// merges identical vertices around a cycle of boundary edges.
///
/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph`.
/// @tparam NamedParameter a sequence of \ref pmp_namedparameters "Named Parameters".
///
/// @param h a halfedge that belongs to a boundary cycle.
/// @param pm the polygon mesh which contains the boundary cycle.
/// @param np optional parameter of \ref pmp_namedparameters "Named Parameters" listed below.
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map}
/// the property map with the points associated to the vertices of `pm`.
/// If this parameter is omitted, an internal property map for
/// `CGAL::vertex_point_t` should be available in `PolygonMesh`
/// \cgalParamEnd
/// \cgalNamedParamsEnd
template <class PolygonMesh, class NamedParameter>
void merge_duplicated_vertices_in_boundary_cycle(
typename boost::graph_traits<PolygonMesh>::halfedge_descriptor h,
PolygonMesh& pm,
const NamedParameter& np)
{
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename GetVertexPointMap<PolygonMesh, NamedParameter>::const_type Vpm;
Vpm vpm = choose_param(get_param(np, internal_np::vertex_point),
get_const_property_map(vertex_point, pm));
// collect all the halfedges of the cycle
std::vector< std::pair<halfedge_descriptor, std::size_t> > cycle_hedges;
halfedge_descriptor start=h;
std::size_t index=0;
do{
cycle_hedges.push_back( std::make_pair(h, index) );
h=next(h, pm);
++index;
}while(start!=h);
std::vector< std::vector<halfedge_descriptor> > hedges_with_identical_point_target;
internal::detect_identical_mergeable_vertices(cycle_hedges, hedges_with_identical_point_target, pm, vpm);
BOOST_FOREACH(const std::vector<halfedge_descriptor>& hedges,
hedges_with_identical_point_target)
{
start=hedges.front();
// hedges are sorted along the cycle
internal::merge_vertices_in_range(hedges, pm);
}
}
/// \ingroup PMP_repairing_grp
/// extracts boundary cycles and merges the duplicated vertices of each cycle.
///
/// @tparam PolygonMesh a model of `FaceListGraph` and `MutableFaceGraph`.
/// @tparam NamedParameter a sequence of \ref pmp_namedparameters "Named Parameters".
///
/// @param pm the polygon mesh which contains the cycles.
/// @param np optional parameter of \ref pmp_namedparameters "Named Parameters" listed below.
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map}
/// the property map with the points associated to the vertices of `pm`.
/// If this parameter is omitted, an internal property map for
/// `CGAL::vertex_point_t` should be available in `PolygonMesh`
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
/// \sa `merge_duplicated_vertices_in_boundary_cycle()`
template <class PolygonMesh, class NamedParameter>
void merge_duplicated_vertices_in_boundary_cycles( PolygonMesh& pm,
const NamedParameter& np)
{
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
std::vector<halfedge_descriptor> cycles;
extract_boundary_cycles(pm, std::back_inserter(cycles));
BOOST_FOREACH(halfedge_descriptor h, cycles)
merge_duplicated_vertices_in_boundary_cycle(h, pm, np);
}
template <class PolygonMesh>
void merge_duplicated_vertices_in_boundary_cycles(PolygonMesh& pm)
{
merge_duplicated_vertices_in_boundary_cycles(pm, parameters::all_default());
}
template <class PolygonMesh>
void merge_duplicated_vertices_in_boundary_cycle(
typename boost::graph_traits<PolygonMesh>::halfedge_descriptor h,
PolygonMesh& pm)
{
merge_duplicated_vertices_in_boundary_cycle(h, pm, parameters::all_default());
}
} } // end of CGAL::Polygon_mesh_processing
#endif //CGAL_POLYGON_MESH_PROCESSING_MERGE_BORDER_VERTICES_H

View File

@ -174,10 +174,10 @@ void random_perturbation(VertexRange vertices
typedef typename boost::lookup_named_param_def <
internal_np::vertex_is_constrained_t,
NamedParameters,
internal::No_constraint_pmap<vertex_descriptor>//default
Constant_property_map<vertex_descriptor, bool> // default
> ::type VCMap;
VCMap vcmap = choose_param(get_param(np, internal_np::vertex_is_constrained),
internal::No_constraint_pmap<vertex_descriptor>());
Constant_property_map<vertex_descriptor, bool>(false));
unsigned int seed = choose_param(get_param(np, internal_np::random_seed), -1);
bool do_project = choose_param(get_param(np, internal_np::do_project), true);

View File

@ -163,6 +163,7 @@ void isotropic_remeshing(const FaceRange& faces
boost::is_default_param(get_param(np, internal_np::projection_functor));
typedef typename GetGeomTraits<PM, NamedParameters>::type GT;
GT gt = choose_param(get_param(np, internal_np::geom_traits), GT());
typedef typename GetVertexPointMap<PM, NamedParameters>::type VPMap;
VPMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
@ -175,18 +176,18 @@ void isotropic_remeshing(const FaceRange& faces
typedef typename boost::lookup_named_param_def <
internal_np::edge_is_constrained_t,
NamedParameters,
internal::No_constraint_pmap<edge_descriptor>//default
Constant_property_map<edge_descriptor, bool> // default (no constraint pmap)
> ::type ECMap;
ECMap ecmap = choose_param(get_param(np, internal_np::edge_is_constrained)
, internal::No_constraint_pmap<edge_descriptor>());
ECMap ecmap = choose_param(get_param(np, internal_np::edge_is_constrained),
Constant_property_map<edge_descriptor, bool>(false));
typedef typename boost::lookup_named_param_def <
internal_np::vertex_is_constrained_t,
NamedParameters,
internal::No_constraint_pmap<vertex_descriptor>//default
Constant_property_map<vertex_descriptor, bool> // default (no constraint pmap)
> ::type VCMap;
VCMap vcmap = choose_param(get_param(np, internal_np::vertex_is_constrained),
internal::No_constraint_pmap<vertex_descriptor>());
Constant_property_map<vertex_descriptor, bool>(false));
bool protect = choose_param(get_param(np, internal_np::protect_constraints), false);
typedef typename boost::lookup_named_param_def <
@ -227,7 +228,7 @@ void isotropic_remeshing(const FaceRange& faces
#endif
typename internal::Incremental_remesher<PM, VPMap, GT, ECMap, VCMap, FPMap, FIMap>
remesher(pmesh, vpmap, protect, ecmap, vcmap, fpmap, fimap, need_aabb_tree);
remesher(pmesh, vpmap, gt, protect, ecmap, vcmap, fpmap, fimap, need_aabb_tree);
remesher.init_remeshing(faces);
#ifdef CGAL_PMP_REMESHING_VERBOSE
@ -340,6 +341,8 @@ void split_long_edges(const EdgeRange& edges
using boost::get_param;
typedef typename GetGeomTraits<PM, NamedParameters>::type GT;
GT gt = choose_param(get_param(np, internal_np::geom_traits), GT());
typedef typename GetVertexPointMap<PM, NamedParameters>::type VPMap;
VPMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
get_property_map(vertex_point, pmesh));
@ -351,22 +354,21 @@ void split_long_edges(const EdgeRange& edges
typedef typename boost::lookup_named_param_def <
internal_np::edge_is_constrained_t,
NamedParameters,
internal::No_constraint_pmap<edge_descriptor>//default
Constant_property_map<edge_descriptor, bool> // default (no constraint pmap)
> ::type ECMap;
ECMap ecmap = choose_param(get_param(np, internal_np::edge_is_constrained),
internal::No_constraint_pmap<edge_descriptor>());
Constant_property_map<edge_descriptor, bool>(false));
typename internal::Incremental_remesher<PM, VPMap, GT, ECMap,
internal::No_constraint_pmap<vertex_descriptor>,
Constant_property_map<vertex_descriptor, bool>, // no constraint pmap
internal::Connected_components_pmap<PM, FIMap>,
FIMap
>
remesher(pmesh, vpmap, false/*protect constraints*/
, ecmap
, internal::No_constraint_pmap<vertex_descriptor>()
, internal::Connected_components_pmap<PM, FIMap>(faces(pmesh), pmesh, ecmap, fimap, false)
, fimap
, false/*need aabb_tree*/);
remesher(pmesh, vpmap, gt, false/*protect constraints*/, ecmap,
Constant_property_map<vertex_descriptor, bool>(false),
internal::Connected_components_pmap<PM, FIMap>(faces(pmesh), pmesh, ecmap, fimap, false),
fimap,
false/*need aabb_tree*/);
remesher.split_long_edges(edges, max_length);
}

View File

@ -24,12 +24,9 @@
#include <CGAL/license/Polygon_mesh_processing/repair.h>
#include <set>
#include <vector>
#include <boost/algorithm/minmax_element.hpp>
#include <CGAL/boost/graph/Euler_operations.h>
#include <CGAL/Union_find.h>
#include <CGAL/property_map.h>
#include <CGAL/algorithm.h>
#include <CGAL/array.h>
@ -40,21 +37,33 @@
#include <CGAL/Polygon_mesh_processing/connected_components.h>
#include <CGAL/Polygon_mesh_processing/border.h>
#include <CGAL/Polygon_mesh_processing/shape_predicates.h>
#include <CGAL/Polygon_mesh_processing/internal/named_function_params.h>
#include <CGAL/Polygon_mesh_processing/internal/named_params_helper.h>
#include <boost/range/has_range_iterator.hpp>
#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
#include <CGAL/IO/OFF_reader.h>
#include <iostream>
#include <fstream>
#endif
#include <boost/algorithm/minmax_element.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/utility/enable_if.hpp>
#include <fstream>
#include <iostream>
#include <map>
#include <set>
#include <utility>
#include <vector>
namespace CGAL{
namespace Polygon_mesh_processing {
namespace debug{
template <class TriangleMesh, class VertexPointMap>
std::ostream& dump_edge_neighborhood(
typename boost::graph_traits<TriangleMesh>::edge_descriptor ed,
@ -142,8 +151,11 @@ namespace debug{
<< vids[ target(next(next(halfedge(f, tm), tm), tm), tm) ] << "\n";
}
}
} //end of namespace debug
namespace internal {
template <class HalfedgeGraph, class VertexPointMap, class Traits>
struct Less_vertex_point{
typedef typename boost::graph_traits<HalfedgeGraph>::vertex_descriptor vertex_descriptor;
@ -157,33 +169,158 @@ struct Less_vertex_point{
}
};
///\cond SKIP_IN_MANUAL
} // end namespace internal
template <class Traits, class TriangleMesh, class VertexPointMap, class OutputIterator>
OutputIterator
degenerate_faces(const TriangleMesh& tm,
const VertexPointMap& vpmap,
const Traits& traits,
OutputIterator out)
/// \ingroup PMP_repairing_grp
/// collects the degenerate edges within a given range of edges.
///
/// @tparam EdgeRange a model of `Range` with value type `boost::graph_traits<TriangleMesh>::%edge_descriptor`
/// @tparam TriangleMesh a model of `HalfedgeGraph`
/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
///
/// @param edges a subset of edges of `tm`
/// @param tm a triangle mesh
/// @param out an output iterator in which the degenerate edges are written
/// @param np optional \ref pmp_namedparameters "Named Parameters" described below
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`.
/// The type of this map is model of `ReadWritePropertyMap`.
/// If this parameter is omitted, an internal property map for
/// `CGAL::vertex_point_t` should be available in `TriangleMesh`
/// \cgalParamEnd
/// \cgalParamBegin{geom_traits} a geometric traits class instance.
/// The traits class must provide the nested type `Point_3`,
/// and the nested functor `Equal_3` to check whether two points are identical.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
template <class EdgeRange, class TriangleMesh, class OutputIterator, class NamedParameters>
OutputIterator degenerate_edges(const EdgeRange& edges,
const TriangleMesh& tm,
OutputIterator out,
const NamedParameters& np)
{
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
BOOST_FOREACH(face_descriptor fd, faces(tm))
typedef typename boost::graph_traits<TriangleMesh>::edge_descriptor edge_descriptor;
BOOST_FOREACH(edge_descriptor ed, edges)
{
if ( is_degenerate_triangle_face(fd, tm, vpmap, traits) )
*out++=fd;
if(is_degenerate_edge(ed, tm, np))
*out++ = ed;
}
return out;
}
template <class EdgeRange, class TriangleMesh, class OutputIterator>
OutputIterator degenerate_edges(const EdgeRange& edges,
const TriangleMesh& tm,
OutputIterator out,
typename boost::enable_if<
typename boost::has_range_iterator<EdgeRange>
>::type* = 0)
{
return degenerate_edges(edges, tm, out, CGAL::parameters::all_default());
}
/// \ingroup PMP_repairing_grp
/// calls the function `degenerate_edges()` with the range: `edges(tm)`.
///
/// See above for the comprehensive description of the parameters.
///
template <class TriangleMesh, class OutputIterator, class NamedParameters>
OutputIterator degenerate_edges(const TriangleMesh& tm,
OutputIterator out,
const NamedParameters& np
#ifndef DOXYGEN_RUNNING
, typename boost::disable_if<
boost::has_range_iterator<TriangleMesh>
>::type* = 0
#endif
)
{
return degenerate_edges(edges(tm), tm, out, np);
}
template <class TriangleMesh, class OutputIterator>
OutputIterator
degenerate_faces(const TriangleMesh& tm, OutputIterator out)
degenerate_edges(const TriangleMesh& tm, OutputIterator out)
{
typedef typename boost::property_map<TriangleMesh, CGAL::vertex_point_t>::type Vpm;
typedef typename boost::property_traits<Vpm>::value_type Point;
typedef typename Kernel_traits<Point>::Kernel Kernel;
return degenerate_edges(edges(tm), tm, out, CGAL::parameters::all_default());
}
return degenerate_faces(tm, get(vertex_point, tm), Kernel(), out);
/// \ingroup PMP_repairing_grp
/// collects the degenerate faces within a given range of faces.
///
/// @tparam FaceRange a model of `Range` with value type `boost::graph_traits<TriangleMesh>::%face_descriptor`
/// @tparam TriangleMesh a model of `FaceGraph`
/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
///
/// @param faces a subset of faces of `tm`
/// @param tm a triangle mesh
/// @param out an output iterator in which the degenerate faces are put
/// @param np optional \ref pmp_namedparameters "Named Parameters" described below
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`.
/// The type of this map is model of `ReadWritePropertyMap`.
/// If this parameter is omitted, an internal property map for
/// `CGAL::vertex_point_t` should be available in `TriangleMesh`
/// \cgalParamEnd
/// \cgalParamBegin{geom_traits} a geometric traits class instance.
/// The traits class must provide the nested functor `Collinear_3`
/// to check whether three points are collinear.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
template <class FaceRange, class TriangleMesh, class OutputIterator, class NamedParameters>
OutputIterator degenerate_faces(const FaceRange& faces,
const TriangleMesh& tm,
OutputIterator out,
const NamedParameters& np)
{
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
BOOST_FOREACH(face_descriptor fd, faces)
{
if(is_degenerate_triangle_face(fd, tm, np))
*out++ = fd;
}
return out;
}
template <class FaceRange, class TriangleMesh, class OutputIterator>
OutputIterator degenerate_faces(const FaceRange& faces,
const TriangleMesh& tm,
OutputIterator out,
typename boost::enable_if<
boost::has_range_iterator<FaceRange>
>::type* = 0)
{
return degenerate_faces(faces, tm, out, CGAL::parameters::all_default());
}
/// \ingroup PMP_repairing_grp
/// calls the function `degenerate_faces()` with the range: `faces(tm)`.
///
/// See above for the comprehensive description of the parameters.
///
template <class TriangleMesh, class OutputIterator, class NamedParameters>
OutputIterator degenerate_faces(const TriangleMesh& tm,
OutputIterator out,
const NamedParameters& np
#ifndef DOXYGEN_RUNNING
, typename boost::disable_if<
boost::has_range_iterator<TriangleMesh>
>::type* = 0
#endif
)
{
return degenerate_faces(faces(tm), tm, out, np);
}
template <class TriangleMesh, class OutputIterator>
OutputIterator degenerate_faces(const TriangleMesh& tm, OutputIterator out)
{
return degenerate_faces(faces(tm), tm, out, CGAL::parameters::all_default());
}
// this function remove a border edge even if it does not satisfy the link condition.
@ -365,10 +502,9 @@ remove_a_border_edge(typename boost::graph_traits<TriangleMesh>::edge_descriptor
}
template <class EdgeRange, class TriangleMesh, class NamedParameters>
std::size_t remove_null_edges(
const EdgeRange& edge_range,
TriangleMesh& tmesh,
const NamedParameters& np)
bool remove_degenerate_edges(const EdgeRange& edge_range,
TriangleMesh& tmesh,
const NamedParameters& np)
{
CGAL_assertion(CGAL::is_triangle_mesh(tmesh));
@ -385,27 +521,25 @@ std::size_t remove_null_edges(
typedef typename GetVertexPointMap<TM, NamedParameters>::type VertexPointMap;
VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
get_property_map(vertex_point, tmesh));
typedef typename GetGeomTraits<TM, NamedParameters>::type Traits;
Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits());
std::size_t nb_deg_faces = 0;
bool all_removed=true;
// collect edges of length 0
std::set<edge_descriptor> null_edges_to_remove;
BOOST_FOREACH(edge_descriptor ed, edge_range)
{
if ( traits.equal_3_object()(get(vpmap, target(ed, tmesh)), get(vpmap, source(ed, tmesh))) )
null_edges_to_remove.insert(ed);
}
std::set<edge_descriptor> degenerate_edges_to_remove;
degenerate_edges(edge_range, tmesh, std::inserter(degenerate_edges_to_remove,
degenerate_edges_to_remove.end()));
#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
std::cout << "Found " << null_edges_to_remove.size() << " null edges.\n";
#endif
#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
std::cout << "Found " << degenerate_edges_to_remove.size() << " null edges.\n";
#endif
while (!null_edges_to_remove.empty())
while (!degenerate_edges_to_remove.empty())
{
edge_descriptor ed = *null_edges_to_remove.begin();
null_edges_to_remove.erase(null_edges_to_remove.begin());
edge_descriptor ed = *degenerate_edges_to_remove.begin();
degenerate_edges_to_remove.erase(degenerate_edges_to_remove.begin());
halfedge_descriptor h = halfedge(ed, tmesh);
@ -415,12 +549,12 @@ std::size_t remove_null_edges(
if ( face(h, tmesh)!=GT::null_face() )
{
++nb_deg_faces;
null_edges_to_remove.erase(edge(prev(h, tmesh), tmesh));
degenerate_edges_to_remove.erase(edge(prev(h, tmesh), tmesh));
}
if (face(opposite(h, tmesh), tmesh)!=GT::null_face())
{
++nb_deg_faces;
null_edges_to_remove.erase(edge(prev(opposite(h, tmesh), tmesh), tmesh));
degenerate_edges_to_remove.erase(edge(prev(opposite(h, tmesh), tmesh), tmesh));
}
//now remove the edge
CGAL::Euler::collapse_edge(ed, tmesh);
@ -435,10 +569,40 @@ std::size_t remove_null_edges(
if (is_triangle(hd, tmesh))
{
Euler::fill_hole(hd, tmesh);
null_edges_to_remove.insert(ed);
degenerate_edges_to_remove.insert(ed);
continue;
}
}
else
{
halfedge_descriptor hd = halfedge(ed,tmesh);
// if both vertices are boundary vertices we can't do anything
bool impossible = false;
BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(hd, tmesh))
{
if (is_border(h, tmesh))
{
impossible = true;
break;
}
}
if (impossible)
{
BOOST_FOREACH(halfedge_descriptor h, halfedges_around_source(hd, tmesh))
{
if (is_border(h, tmesh))
{
impossible = true;
break;
}
}
if (impossible)
{
all_removed=false;
continue;
}
}
}
// When the edge does not satisfy the link condition, it means that it cannot be
// collapsed as is. In the following we assume that there is no topological issue
@ -621,7 +785,7 @@ std::size_t remove_null_edges(
// remove edges
BOOST_FOREACH(edge_descriptor ed, edges_to_remove)
{
null_edges_to_remove.erase(ed);
degenerate_edges_to_remove.erase(ed);
remove_edge(ed, tmesh);
}
@ -641,61 +805,60 @@ std::size_t remove_null_edges(
put(vpmap, target(new_hd, tmesh), pt);
BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(new_hd, tmesh))
if ( traits.equal_3_object()(get(vpmap, target(hd, tmesh)), get(vpmap, source(hd, tmesh))) )
null_edges_to_remove.insert(edge(hd, tmesh));
if(is_degenerate_edge(edge(hd, tmesh), tmesh, np))
degenerate_edges_to_remove.insert(edge(hd, tmesh));
CGAL_assertion( is_valid_polygon_mesh(tmesh) );
}
}
return nb_deg_faces;
return all_removed;
}
template <class EdgeRange, class TriangleMesh>
std::size_t remove_null_edges(
const EdgeRange& edge_range,
TriangleMesh& tmesh)
bool remove_degenerate_edges(const EdgeRange& edge_range,
TriangleMesh& tmesh)
{
return remove_null_edges(edge_range, tmesh,
parameters::all_default());
return remove_degenerate_edges(edge_range, tmesh, parameters::all_default());
}
/// \ingroup PMP_repairing_grp
/// removes the degenerate faces from a triangulated surface mesh.
/// A face is considered degenerate if two of its vertices share the same location,
/// or more generally if all its vertices are collinear.
///
/// @pre `CGAL::is_triangle_mesh(tmesh)`
///
/// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph`
/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
///
/// @param tmesh the triangulated surface mesh to be repaired
/// @param np optional \ref pmp_namedparameters "Named Parameters" described below
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`. The type of this map is model of `ReadWritePropertyMap`.
/// If this parameter is omitted, an internal property map for
/// `CGAL::vertex_point_t` must be available in `TriangleMesh`
/// \cgalParamEnd
/// \cgalParamBegin{geom_traits} a geometric traits class instance.
/// The traits class must provide the nested type `Point_3`,
/// and the nested functors :
/// - `Compare_distance_3` to compute the distance between 2 points
/// - `Collinear_are_ordered_along_line_3` to check whether 3 collinear points are ordered
/// - `Collinear_3` to check whether 3 points are collinear
/// - `Less_xyz_3` to compare lexicographically two points
/// - `Equal_3` to check whether 2 points are identical
/// - for each functor Foo, a function `Foo foo_object()`
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
/// \todo the function might not be able to remove all degenerate faces.
/// We should probably do something with the return type.
/// \return number of removed degenerate faces
// \ingroup PMP_repairing_grp
// removes the degenerate faces from a triangulated surface mesh.
// A face is considered degenerate if two of its vertices share the same location,
// or more generally if all its vertices are collinear.
//
// @pre `CGAL::is_triangle_mesh(tmesh)`
//
// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph`
// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
//
// @param tmesh the triangulated surface mesh to be repaired
// @param np optional \ref pmp_namedparameters "Named Parameters" described below
//
// \cgalNamedParamsBegin
// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`.
// The type of this map is model of `ReadWritePropertyMap`.
// If this parameter is omitted, an internal property map for
// `CGAL::vertex_point_t` must be available in `TriangleMesh`
// \cgalParamEnd
// \cgalParamBegin{geom_traits} a geometric traits class instance.
// The traits class must provide the nested type `Point_3`,
// and the nested functors :
// - `Compare_distance_3` to compute the distance between 2 points
// - `Collinear_3` to check whether 3 points are collinear
// - `Less_xyz_3` to compare lexicographically two points
// - `Equal_3` to check whether 2 points are identical
// For each functor `Foo`, a function `Foo foo_object()`
// \cgalParamEnd
// \cgalNamedParamsEnd
//
// \todo the function might not be able to remove all degenerate faces.
// We should probably do something with the return type.
//
/// \return `true` if all degenerate faces were successfully removed, and `false` otherwise.
template <class TriangleMesh, class NamedParameters>
std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
const NamedParameters& np)
bool remove_degenerate_faces( TriangleMesh& tmesh,
const NamedParameters& np)
{
CGAL_assertion(CGAL::is_triangle_mesh(tmesh));
@ -717,8 +880,9 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
typedef typename boost::property_traits<VertexPointMap>::value_type Point_3;
typedef typename boost::property_traits<VertexPointMap>::reference Point_ref;
// First remove edges of length 0
std::size_t nb_deg_faces = remove_null_edges(edges(tmesh), tmesh, np);
bool all_removed = remove_degenerate_edges(edges(tmesh), tmesh, np);
#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
{
@ -731,10 +895,21 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
// Then, remove triangles made of 3 collinear points
std::set<face_descriptor> degenerate_face_set;
BOOST_FOREACH(face_descriptor fd, faces(tmesh))
if ( is_degenerate_triangle_face(fd, tmesh, vpmap, traits) )
degenerate_face_set.insert(fd);
nb_deg_faces+=degenerate_face_set.size();
degenerate_faces(tmesh, std::inserter(degenerate_face_set, degenerate_face_set.begin()), np);
// Ignore faces with null edges
if (!all_removed)
{
BOOST_FOREACH(edge_descriptor ed, edges(tmesh))
{
if ( traits.equal_3_object()(get(vpmap, target(ed, tmesh)), get(vpmap, source(ed, tmesh))) )
{
halfedge_descriptor h = halfedge(ed, tmesh);
if (!is_border(h, tmesh)) degenerate_face_set.erase(face(h, tmesh));
h=opposite(h, tmesh);
if (!is_border(h, tmesh)) degenerate_face_set.erase(face(h, tmesh));
}
}
}
// first remove degree 3 vertices that are part of a cap
// (only the vertex in the middle of the opposite edge)
@ -747,7 +922,7 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_face(halfedge(fd, tmesh), tmesh))
{
vertex_descriptor vd = target(hd, tmesh);
if (degree(vd, tmesh) == 3)
if (degree(vd, tmesh) == 3 && is_border(vd, tmesh)==GT::null_halfedge())
{
vertices_to_remove.insert(vd);
break;
@ -763,7 +938,7 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
degenerate_face_set.erase( face(hd2, tmesh) );
// remove the central vertex and check if the new face is degenerated
hd=CGAL::Euler::remove_center_vertex(hd, tmesh);
if (is_degenerate_triangle_face(face(hd, tmesh), tmesh, vpmap, traits))
if (is_degenerate_triangle_face(face(hd, tmesh), tmesh, np))
{
degenerate_face_set.insert( face(hd, tmesh) );
}
@ -855,15 +1030,17 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
{
Euler::flip_edge(edge_to_flip, tmesh);
}
else
{
all_removed=false;
#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
else{
std::cout << " WARNING: flip is not possible\n";
// \todo Let p and q be the vertices opposite to `edge_to_flip`, and let
// r be the vertex of `edge_to_flip` that is the furthest away from
// the edge `pq`. In that case I think we should remove all the triangles
// so that the triangle pqr is in the mesh.
}
#endif
}
}
}
else
@ -964,7 +1141,7 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
// preliminary step to check if the operation is possible
// sort the boundary points along the common supporting line
// we first need a reference point
typedef Less_vertex_point<TriangleMesh, VertexPointMap, Traits> Less_vertex;
typedef internal::Less_vertex_point<TriangleMesh, VertexPointMap, Traits> Less_vertex;
std::pair<
typename std::set<vertex_descriptor>::iterator,
typename std::set<vertex_descriptor>::iterator > ref_vertices =
@ -1278,10 +1455,9 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
}
}
return nb_deg_faces;
return all_removed;
}
template<class TriangleMesh>
std::size_t remove_degenerate_faces(TriangleMesh& tmesh)
{
@ -1289,52 +1465,207 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh)
CGAL::Polygon_mesh_processing::parameters::all_default());
}
template <class TriangleMesh, class Vpm>
std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, Vpm vpm)
namespace internal {
template <typename G, typename OutputIterator>
struct Vertex_collector
{
typedef typename boost::graph_traits<G>::vertex_descriptor vertex_descriptor;
void collect_vertices(vertex_descriptor v1, vertex_descriptor v2)
{
std::vector<vertex_descriptor>& verts = collections[v1];
if(verts.empty())
verts.push_back(v1);
verts.push_back(v2);
}
void dump(OutputIterator out)
{
typedef std::pair<const vertex_descriptor, std::vector<vertex_descriptor> > Pair_type;
BOOST_FOREACH(const Pair_type& p, collections) {
*out++ = p.second;
}
}
std::map<vertex_descriptor, std::vector<vertex_descriptor> > collections;
};
template <typename G>
struct Vertex_collector<G, Emptyset_iterator>
{
typedef typename boost::graph_traits<G>::vertex_descriptor vertex_descriptor;
void collect_vertices(vertex_descriptor, vertex_descriptor)
{}
void dump(Emptyset_iterator)
{}
};
} // end namespace internal
/// \ingroup PMP_repairing_grp
/// checks whether a vertex of a polygon mesh is non-manifold.
///
/// @tparam PolygonMesh a model of `HalfedgeListGraph`
///
/// @param v a vertex of `pm`
/// @param pm a triangle mesh containing `v`
///
/// \sa `duplicate_non_manifold_vertices()`
///
/// \return `true` if the vertex is non-manifold, `false` otherwise.
template <typename PolygonMesh>
bool is_non_manifold_vertex(typename boost::graph_traits<PolygonMesh>::vertex_descriptor v,
const PolygonMesh& pm)
{
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
boost::unordered_set<halfedge_descriptor> halfedges_handled;
std::size_t incident_null_faces_counter = 0;
BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(v, pm))
{
halfedges_handled.insert(h);
if(CGAL::is_border(h, pm))
++incident_null_faces_counter;
}
if(incident_null_faces_counter > 1)
{
// The vertex is the sole connection between two connected components --> non-manifold
return true;
}
BOOST_FOREACH(halfedge_descriptor h, halfedges(pm))
{
if(v == target(h, pm))
{
// More than one umbrella incident to 'v' --> non-manifold
if(halfedges_handled.count(h) == 0)
return true;
}
}
return false;
}
/// \ingroup PMP_repairing_grp
/// duplicates all the non-manifold vertices of the input mesh.
///
/// @tparam TriangleMesh a model of `HalfedgeListGraph` and `MutableHalfedgeGraph`
/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
///
/// @param tm the triangulated surface mesh to be repaired
/// @param np optional \ref pmp_namedparameters "Named Parameters" described below
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`.
/// The type of this map is model of `ReadWritePropertyMap`.
/// If this parameter is omitted, an internal property map for
/// `CGAL::vertex_point_t` should be available in `TriangleMesh`
/// \cgalParamEnd
/// \cgalParamBegin{vertex_is_constrained_map} a writable property map with `vertex_descriptor`
/// as key and `bool` as `value_type`. `put(pmap, v, true)` will be called for each duplicated
/// vertices, as well as the original non-manifold vertex in the input mesh.
/// \cgalParamEnd
/// \cgalParamBegin{output_iterator} a model of `OutputIterator` with value type
/// `std::vector<vertex_descriptor>`. The first vertex of each vector is a non-manifold vertex
/// of the input mesh, followed by the new vertices that were created to fix this precise
/// non-manifold configuration.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
/// \return the number of vertices created.
template <typename TriangleMesh, typename NamedParameters>
std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm,
const NamedParameters& np)
{
CGAL_assertion(CGAL::is_triangle_mesh(tm));
using boost::get_param;
using boost::choose_param;
typedef boost::graph_traits<TriangleMesh> GT;
typedef typename GT::vertex_descriptor vertex_descriptor;
typedef typename GT::halfedge_descriptor halfedge_descriptor;
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::type VertexPointMap;
VertexPointMap vpm = choose_param(get_param(np, internal_np::vertex_point),
get_property_map(vertex_point, tm));
typedef typename boost::lookup_named_param_def <
internal_np::vertex_is_constrained_t,
NamedParameters,
Constant_property_map<vertex_descriptor, bool> // default (no constraint pmap)
> ::type VerticesMap;
VerticesMap cmap
= choose_param(get_param(np, internal_np::vertex_is_constrained),
Constant_property_map<vertex_descriptor, bool>(false));
typedef typename boost::lookup_named_param_def <
internal_np::output_iterator_t,
NamedParameters,
Emptyset_iterator
> ::type Output_iterator;
Output_iterator out
= choose_param(get_param(np, internal_np::output_iterator),
Emptyset_iterator());
internal::Vertex_collector<TriangleMesh, Output_iterator> dmap;
boost::unordered_set<vertex_descriptor> vertices_handled;
boost::unordered_set<halfedge_descriptor> halfedges_handled;
std::size_t nb_new_vertices=0;
std::size_t nb_new_vertices = 0;
std::vector<halfedge_descriptor> non_manifold_cones;
BOOST_FOREACH(halfedge_descriptor h, halfedges(tm))
{
if (halfedges_handled.insert(h).second)
// If 'h' is not visited yet, we walk around the target of 'h' and mark these
// halfedges as visited. Thus, if we are here and the target is already marked as visited,
// it means that the vertex is non manifold.
if(halfedges_handled.insert(h).second)
{
vertex_descriptor vd = target(h, tm);
if ( !vertices_handled.insert(vd).second )
if(!vertices_handled.insert(vd).second)
{
put(cmap, vd, true); // store the originals
non_manifold_cones.push_back(h);
}
else
{
set_halfedge(vd, h, tm);
halfedge_descriptor start=opposite(next(h, tm), tm);
h=start;
do{
}
halfedge_descriptor start = opposite(next(h, tm), tm);
h = start;
do
{
halfedges_handled.insert(h);
h=opposite(next(h, tm), tm);
}while(h!=start);
h = opposite(next(h, tm), tm);
}
while(h != start);
}
}
if (!non_manifold_cones.empty()) {
if(!non_manifold_cones.empty())
{
BOOST_FOREACH(halfedge_descriptor h, non_manifold_cones)
{
halfedge_descriptor start = h;
vertex_descriptor new_vd = add_vertex(tm);
++nb_new_vertices;
put(cmap, new_vd, true); // store the duplicates
dmap.collect_vertices(target(h, tm), new_vd);
put(vpm, new_vd, get(vpm, target(h, tm)));
set_halfedge(new_vd, h, tm);
do{
do
{
set_target(h, new_vd, tm);
h=opposite(next(h, tm), tm);
} while(h!=start);
h = opposite(next(h, tm), tm);
}
while(h != start);
}
dmap.dump(out);
}
return nb_new_vertices;
@ -1343,12 +1674,9 @@ std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, Vpm vpm)
template <class TriangleMesh>
std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm)
{
return duplicate_non_manifold_vertices(tm, get(vertex_point, tm));
return duplicate_non_manifold_vertices(tm, parameters::all_default());
}
/// \endcond
/// \ingroup PMP_repairing_grp
/// removes the isolated vertices from any polygon mesh.
/// A vertex is considered isolated if it is not incident to any simplex

View File

@ -0,0 +1,358 @@
// Copyright (c) 2015, 2018 GeometryFactory (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
// You can redistribute it and/or modify it under the terms of the GNU
// General Public License as published by the Free Software Foundation,
// either version 3 of the License, or (at your option) any later version.
//
// Licensees holding a valid commercial license may use this file in
// accordance with the commercial license agreement provided with the software.
//
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0+
//
//
// Author(s) : Konstantinos Katrioplas,
// Mael Rouxel-Labbé
#ifndef CGAL_POLYGON_MESH_PROCESSING_SHAPE_PREDICATES_H
#define CGAL_POLYGON_MESH_PROCESSING_SHAPE_PREDICATES_H
#include <CGAL/license/Polygon_mesh_processing/repair.h>
#include <CGAL/Polygon_mesh_processing/internal/named_function_params.h>
#include <CGAL/Polygon_mesh_processing/internal/named_params_helper.h>
#include <CGAL/array.h>
#include <CGAL/boost/graph/iterator.h>
#include <CGAL/boost/graph/helpers.h>
#include <boost/foreach.hpp>
#include <boost/graph/graph_traits.hpp>
#include <limits>
#include <map>
#include <utility>
#include <vector>
namespace CGAL {
namespace Polygon_mesh_processing {
/// \ingroup PMP_repairing_grp
/// checks whether an edge is degenerate.
/// An edge is considered degenerate if the geometric positions of its two extremities are identical.
///
/// @tparam PolygonMesh a model of `HalfedgeGraph`
/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
///
/// @param e an edge of `pm`
/// @param pm polygon mesh containing `e`
/// @param np optional \ref pmp_namedparameters "Named Parameters" described below
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pm`.
/// The type of this map is model of `ReadWritePropertyMap`.
/// If this parameter is omitted, an internal property map for
/// `CGAL::vertex_point_t` should be available in `PolygonMesh`
/// \cgalParamEnd
/// \cgalParamBegin{geom_traits} a geometric traits class instance.
/// The traits class must provide the nested type `Point_3`,
/// and the nested functor `Equal_3` to check whether two points are identical.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
/// \sa `degenerate_edges()`
///
/// \return `true` if the edge `e` is degenerate, `false` otherwise.
template <typename PolygonMesh, typename NamedParameters>
bool is_degenerate_edge(typename boost::graph_traits<PolygonMesh>::edge_descriptor e,
const PolygonMesh& pm,
const NamedParameters& np)
{
using boost::get_param;
using boost::choose_param;
typedef typename GetVertexPointMap<PolygonMesh, NamedParameters>::const_type VertexPointMap;
VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
get_const_property_map(vertex_point, pm));
typedef typename GetGeomTraits<PolygonMesh, NamedParameters>::type Traits;
Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits());
return traits.equal_3_object()(get(vpmap, source(e, pm)), get(vpmap, target(e, pm)));
}
template <typename PolygonMesh>
bool is_degenerate_edge(typename boost::graph_traits<PolygonMesh>::edge_descriptor e,
const PolygonMesh& pm)
{
return is_degenerate_edge(e, pm, parameters::all_default());
}
/// \ingroup PMP_repairing_grp
/// checks whether a triangle face is degenerate.
/// A triangle face is considered degenerate if the geometric positions of its vertices are collinear.
///
/// @tparam TriangleMesh a model of `FaceGraph`
/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
///
/// @param f a triangle face of `tm`
/// @param tm a triangle mesh containing `f`
/// @param np optional \ref pmp_namedparameters "Named Parameters" described below
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`.
/// The type of this map is model of `ReadWritePropertyMap`.
/// If this parameter is omitted, an internal property map for
/// `CGAL::vertex_point_t` should be available in `TriangleMesh`
/// \cgalParamEnd
/// \cgalParamBegin{geom_traits} a geometric traits class instance.
/// The traits class must provide the nested functor `Collinear_3`
/// to check whether three points are collinear.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
/// \sa `degenerate_faces()`
///
/// \return `true` if the face `f` is degenerate, `false` otherwise.
template <typename TriangleMesh, typename NamedParameters>
bool is_degenerate_triangle_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
const TriangleMesh& tm,
const NamedParameters& np)
{
CGAL_precondition(CGAL::is_triangle(halfedge(f, tm), tm));
using boost::get_param;
using boost::choose_param;
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::const_type VertexPointMap;
VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
get_const_property_map(vertex_point, tm));
typedef typename GetGeomTraits<TriangleMesh, NamedParameters>::type Traits;
Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits());
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor h = halfedge(f, tm);
return traits.collinear_3_object()(get(vpmap, source(h, tm)),
get(vpmap, target(h, tm)),
get(vpmap, target(next(h, tm), tm)));
}
template <typename TriangleMesh>
bool is_degenerate_triangle_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
const TriangleMesh& tm)
{
return CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, tm, parameters::all_default());
}
/// \ingroup PMP_repairing_grp
/// checks whether a triangle face is needle.
/// A triangle is said to be a <i>needle</i> if its longest edge is much longer than its shortest edge.
///
/// @tparam TriangleMesh a model of `FaceGraph`
/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
///
/// @param f a triangle face of `tm`
/// @param tm triangle mesh containing `f`
/// @param threshold a bound on the ratio of the longest edge length and the shortest edge length
/// @param np optional \ref pmp_namedparameters "Named Parameters" described below
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`.
/// The type of this map is model of `ReadWritePropertyMap`.
/// If this parameter is omitted, an internal property map for
/// `CGAL::vertex_point_t` should be available in `TriangleMesh`
/// \cgalParamEnd
/// \cgalParamBegin{geom_traits} a geometric traits class instance.
/// The traits class must provide the nested type `FT` and
/// the nested functor `Compute_squared_distance_3`.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
/// \return the shortest halfedge if the triangle face is a needle, and a null halfedge otherwise.
/// If the face contains degenerate edges, a halfedge corresponding to one of these edges is returned.
template <typename TriangleMesh, typename NamedParameters>
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor
is_needle_triangle_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
const TriangleMesh& tm,
const double threshold,
const NamedParameters& np)
{
CGAL_precondition(threshold >= 1.);
using boost::get_param;
using boost::choose_param;
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::const_type VertexPointMap;
VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
get_const_property_map(vertex_point, tm));
typedef typename GetGeomTraits<TriangleMesh, NamedParameters>::type Traits;
Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits());
typedef typename Traits::FT FT;
CGAL::Halfedge_around_face_iterator<TriangleMesh> hit, hend;
boost::tie(hit, hend) = CGAL::halfedges_around_face(halfedge(f, tm), tm);
CGAL_precondition(std::distance(hit, hend) == 3);
const halfedge_descriptor h0 = *hit++;
FT sq_length = traits.compute_squared_distance_3_object()(get(vpmap, source(h0, tm)),
get(vpmap, target(h0, tm)));
FT min_sq_length = sq_length, max_sq_length = sq_length;
halfedge_descriptor min_h = h0;
for(; hit!=hend; ++hit)
{
const halfedge_descriptor h = *hit;
sq_length = traits.compute_squared_distance_3_object()(get(vpmap, source(h, tm)),
get(vpmap, target(h, tm)));
if(max_sq_length < sq_length)
max_sq_length = sq_length;
if(min_sq_length > sq_length)
{
min_h = h;
min_sq_length = sq_length;
}
}
if(min_sq_length == 0)
return min_h;
const FT sq_threshold = threshold * threshold;
if(max_sq_length / min_sq_length >= sq_threshold)
{
CGAL_assertion(min_h != boost::graph_traits<TriangleMesh>::null_halfedge());
return min_h;
}
else
return boost::graph_traits<TriangleMesh>::null_halfedge();
}
template <typename TriangleMesh>
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor
is_needle_triangle_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
const TriangleMesh& tm,
const double threshold)
{
return is_needle_triangle_face(f, tm, threshold, parameters::all_default());
}
/// \ingroup PMP_repairing_grp
/// checks whether a triangle face is a cap.
/// A triangle is said to be a <i>cap</i> if one of the its angles is close to `180` degrees.
///
/// @tparam TriangleMesh a model of `FaceGraph`
/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
///
/// @param f a triangle face of `tm`
/// @param tm triangle mesh containing `f`
/// @param threshold the cosine of a minimum angle such that if `f` has an angle greater than this bound,
/// it is a cap. The threshold is in range `[-1 0]` and corresponds to an angle
/// between `90` and `180` degrees.
/// @param np optional \ref pmp_namedparameters "Named Parameters" described below
///
/// \cgalNamedParamsBegin
/// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `tm`.
/// The type of this map is model of `ReadWritePropertyMap`.
/// If this parameter is omitted, an internal property map for
/// `CGAL::vertex_point_t` should be available in `TriangleMesh`
/// \cgalParamEnd
/// \cgalParamBegin{geom_traits} a geometric traits class instance.
/// The traits class must provide the nested type `Point_3` and
/// the nested functors `Compute_squared_distance_3`, `Construct_vector_3`,
/// and `Compute_scalar_product_3`.
/// \cgalParamEnd
/// \cgalNamedParamsEnd
///
/// \return the halfedge opposite of the largest angle if the face is a cap, and a null halfedge otherwise.
template <typename TriangleMesh, typename NamedParameters>
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor
is_cap_triangle_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
const TriangleMesh& tm,
const double threshold,
const NamedParameters& np)
{
CGAL_precondition(CGAL::is_triangle_mesh(tm));
CGAL_precondition(threshold >= -1.);
CGAL_precondition(threshold <= 0.);
using boost::get_param;
using boost::choose_param;
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::const_type VertexPointMap;
VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
get_const_property_map(vertex_point, tm));
typedef typename GetGeomTraits<TriangleMesh, NamedParameters>::type Traits;
Traits traits = choose_param(get_param(np, internal_np::geom_traits), Traits());
typedef typename Traits::FT FT;
typedef typename Traits::Vector_3 Vector_3;
const FT sq_threshold = threshold * threshold;
const halfedge_descriptor h0 = halfedge(f, tm);
cpp11::array<FT, 3> sq_lengths;
int pos = 0;
BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(h0, tm))
{
const FT sq_d = traits.compute_squared_distance_3_object()(get(vpmap, source(h, tm)),
get(vpmap, target(h, tm)));
// If even one edge is degenerate, it cannot be a cap
if(sq_d == 0)
return boost::graph_traits<TriangleMesh>::null_halfedge();
sq_lengths[pos++] = sq_d;
}
pos = 0;
BOOST_FOREACH(halfedge_descriptor h, halfedges_around_face(h0, tm))
{
const vertex_descriptor v0 = source(h, tm);
const vertex_descriptor v1 = target(h, tm);
const vertex_descriptor v2 = target(next(h, tm), tm);
const Vector_3 a = traits.construct_vector_3_object()(get(vpmap, v1), get(vpmap, v2));
const Vector_3 b = traits.construct_vector_3_object()(get(vpmap, v1), get(vpmap, v0));
const FT dot_ab = traits.compute_scalar_product_3_object()(a, b);
const bool neg_sp = (dot_ab <= 0);
const FT sq_a = sq_lengths[(pos+1)%3];
const FT sq_b = sq_lengths[pos];
const FT sq_cos = dot_ab * dot_ab / (sq_a * sq_b);
if(neg_sp && sq_cos >= sq_threshold)
return prev(h, tm);
++pos;
}
return boost::graph_traits<TriangleMesh>::null_halfedge();
}
template <typename TriangleMesh>
typename boost::graph_traits<TriangleMesh>::halfedge_descriptor
is_cap_triangle_face(typename boost::graph_traits<TriangleMesh>::face_descriptor f,
const TriangleMesh& tm,
const double threshold)
{
return is_cap_triangle_face(f, tm, threshold, parameters::all_default());
}
} } // end namespaces CGAL and PMP
#endif // CGAL_POLYGON_MESH_PROCESSING_SHAPE_PREDICATES_H

View File

@ -49,6 +49,8 @@
#include <CGAL/Polygon_mesh_processing/distance.h>
#include <CGAL/Polygon_mesh_processing/intersection.h>
#include <CGAL/Polygon_mesh_processing/transform.h>
#include <CGAL/Polygon_mesh_processing/shape_predicates.h>
#include <CGAL/Polygon_mesh_processing/merge_border_vertices.h>
// the named parameter header being not documented the doc is put here for now
#ifdef DOXYGEN_RUNNING

View File

@ -101,6 +101,9 @@ endif()
create_single_source_cgal_program("test_orient_cc.cpp")
create_single_source_cgal_program("test_pmp_transform.cpp")
create_single_source_cgal_program("extrude_test.cpp")
create_single_source_cgal_program("remove_degeneracies_test.cpp")
create_single_source_cgal_program("test_merging_border_vertices.cpp")
create_single_source_cgal_program("test_shape_predicates.cpp")
if( TBB_FOUND )
CGAL_target_use_TBB(test_pmp_distance)

View File

@ -0,0 +1,95 @@
OFF
47 45 0
-0.026804100000000001 -0.30492799999999998 0.142542
-0.018234400000000001 -0.28125499999999998 0.15192700000000001
-0.074329900000000004 -0.27701599999999998 0.17852999999999999
-0.075232300000000002 -0.238787 0.19677600000000001
-0.092496499999999995 -0.26196000000000003 0.18681900000000001
-0.107975 -0.220055 0.202264
-0.098042799999999999 -0.24015500000000001 0.197738
-0.076830499999999996 -0.20389099999999999 0.20217599999999999
-0.10664999999999999 -0.18507100000000001 0.204627
-0.048507700000000001 -0.30671799999999999 0.14971999999999999
-0.10985300000000001 -0.27012399999999998 0.16877500000000001
0.020159400000000001 -0.26358599999999999 0.11985899999999999
-0.0820192 -0.303394 0.145977
-0.11151800000000001 -0.28692499999999999 0.148566
0.033791700000000001 -0.209923 0.115845
0.019949499999999998 -0.19298699999999999 0.125832
0.00080011299999999997 -0.19967099999999999 0.13874900000000001
0.0102764 -0.16375000000000001 0.13344700000000001
0.015325999999999999 -0.12934699999999999 0.14213100000000001
0.043303899999999999 -0.239647 0.099889199999999997
-0.010328800000000001 -0.10643 0.14183100000000001
-0.032521000000000001 -0.10838299999999999 0.13683899999999999
-0.085985000000000006 -0.101282 0.15809300000000001
-0.108849 -0.11043600000000001 0.16941700000000001
-0.052558500000000001 -0.097836599999999996 0.13975499999999999
-0.10316599999999999 -0.15746599999999999 0.19631499999999999
-0.112763 -0.133107 0.183753
-0.063495300000000005 -0.15489 0.17952499999999999
-0.064211199999999996 -0.12604799999999999 0.16286
-0.086169300000000004 -0.13996600000000001 0.183699
-0.063495300000000005 -0.15489 0.17952499999999999
-0.023309900000000001 -0.17152600000000001 0.15354100000000001
-0.0254547 -0.133829 0.14063300000000001
-0.079254099999999994 -0.17391000000000001 0.197354
-0.0458796 -0.210094 0.18626999999999999
-0.063495300000000005 -0.15489 0.17952499999999999
-0.0097084900000000002 -0.22793099999999999 0.15407299999999999
-0.0458796 -0.210094 0.18626999999999999
-0.0458796 -0.210094 0.18626999999999999
-0.075232300000000002 -0.238787 0.19677600000000001
-0.049308200000000003 -0.25528899999999999 0.18343899999999999
-0.0218198 -0.25775500000000001 0.16445000000000001
-0.049308200000000003 -0.25528899999999999 0.18343899999999999
-0.041782300000000001 -0.28198800000000002 0.16825000000000001
0.00026201499999999999 -0.25824399999999997 0.14286599999999999
0.015174200000000001 -0.229959 0.128466
-0.0097084900000000002 -0.22793099999999999 0.15407299999999999
3 43 0 1
3 2 3 4
3 5 6 3
3 7 5 3
3 5 7 8
3 0 43 9
3 10 2 4
3 1 44 41
3 11 45 44
3 12 9 2
3 13 12 2
3 14 15 45
3 16 45 15
3 43 1 41
3 17 16 15
3 16 31 36
3 31 16 17
3 17 18 32
3 11 19 45
3 43 2 9
3 18 20 32
3 20 21 32
3 13 2 10
3 29 28 22
3 45 19 14
3 23 29 22
3 24 22 28
3 21 24 28
3 32 21 28
3 33 29 25
3 8 33 25
3 8 7 33
3 26 29 23
3 34 33 7
3 25 29 26
3 4 3 6
3 31 17 32
3 29 27 28
3 32 30 31
3 34 35 33
3 31 37 36
3 40 38 39
3 41 42 43
3 45 46 44
3 44 46 41

View File

@ -0,0 +1,20 @@
OFF
8 8 0
0 1 0
1 0 0
0 0 0
0 0 1
2 1 0
2 0 0
2 0 -1
1 0 0
3 0 1 2
3 2 3 0
3 1 3 2
3 0 3 1
3 7 5 4
3 7 6 5
3 4 6 7
3 5 6 4

View File

@ -0,0 +1,17 @@
OFF
9 3 0
0 0 0
1 0 0
1 1 0
0 0 1
1 0 1
10 10 1
0 0 2
1 0 2
-0.99619469809 0.08715574274 2
3 0 1 2
3 3 4 5
3 6 7 8

View File

@ -0,0 +1,6 @@
OFF
3 1 0
0 0 0
1 0 0
0 0 0
3 0 1 2

View File

@ -11,11 +11,34 @@
//the last test (on trihole.off) does not terminate
//
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
namespace PMP = CGAL::Polygon_mesh_processing;
typedef CGAL::Surface_mesh<K::Point_3> Surface_mesh;
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
void fix(const char* fname)
typedef CGAL::Surface_mesh<K::Point_3> Surface_mesh;
typedef boost::graph_traits<Surface_mesh>::edge_descriptor edge_descriptor;
typedef boost::graph_traits<Surface_mesh>::face_descriptor face_descriptor;
void detect_degeneracies(const Surface_mesh& mesh)
{
std::vector<face_descriptor> dfaces;
PMP::degenerate_faces(mesh, std::back_inserter(dfaces));
PMP::degenerate_faces(faces(mesh), mesh, std::back_inserter(dfaces));
PMP::degenerate_faces(mesh, std::back_inserter(dfaces), CGAL::parameters::all_default());
PMP::degenerate_faces(faces(mesh), mesh, std::back_inserter(dfaces), CGAL::parameters::all_default());
assert(!dfaces.empty());
std::set<edge_descriptor> dedges;
PMP::degenerate_edges(mesh, std::inserter(dedges, dedges.end()));
PMP::degenerate_edges(edges(mesh), mesh, std::inserter(dedges, dedges.begin()));
PMP::degenerate_edges(mesh, std::inserter(dedges, dedges.end()), CGAL::parameters::all_default());
PMP::degenerate_edges(edges(mesh), mesh, std::inserter(dedges, dedges.begin()), CGAL::parameters::all_default());
assert(dedges.empty());
}
void fix_degeneracies(const char* fname)
{
std::ifstream input(fname);
@ -24,20 +47,22 @@ void fix(const char* fname)
std::cerr << fname << " is not a valid off file.\n";
exit(1);
}
CGAL::Polygon_mesh_processing::remove_degenerate_faces(mesh);
detect_degeneracies(mesh);
CGAL::Polygon_mesh_processing::remove_degenerate_faces(mesh);
assert( CGAL::is_valid_polygon_mesh(mesh) );
}
int main()
{
fix("data_degeneracies/degtri_2dt_1edge_split_twice.off");
fix("data_degeneracies/degtri_four-2.off");
fix("data_degeneracies/degtri_four.off");
fix("data_degeneracies/degtri_on_border.off");
fix("data_degeneracies/degtri_three.off");
fix("data_degeneracies/degtri_single.off");
fix("data_degeneracies/trihole.off");
fix_degeneracies("data_degeneracies/degtri_2dt_1edge_split_twice.off");
fix_degeneracies("data_degeneracies/degtri_four-2.off");
fix_degeneracies("data_degeneracies/degtri_four.off");
fix_degeneracies("data_degeneracies/degtri_on_border.off");
fix_degeneracies("data_degeneracies/degtri_three.off");
fix_degeneracies("data_degeneracies/degtri_single.off");
fix_degeneracies("data_degeneracies/trihole.off");
return 0;
}

View File

@ -0,0 +1,54 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/merge_border_vertices.h>
#include <CGAL/boost/graph/graph_traits_Surface_mesh.h>
#include <fstream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Surface_mesh<K::Point_3> Surface_mesh;
void test_merge_duplicated_vertices_in_boundary_cycles(const char* fname,
std::size_t expected_nb_vertices)
{
std::ifstream input(fname);
Surface_mesh mesh;
if (!input || !(input >> mesh) || mesh.is_empty()) {
std::cerr << fname << " is not a valid off file.\n";
exit(1);
}
std::cout << "Testing merging in cycles " << fname << "\n";
std::cout << " input mesh has " << vertices(mesh).size() << " vertices.\n";
CGAL::Polygon_mesh_processing::merge_duplicated_vertices_in_boundary_cycles(mesh);
std::cout << " output mesh has " << vertices(mesh).size() << " vertices.\n";
assert(expected_nb_vertices==0 ||
expected_nb_vertices == vertices(mesh).size());
if (expected_nb_vertices==0)
{
std::cout << "writting output to out1.off\n";
std::ofstream output("out1.off");
output << std::setprecision(17);
output << mesh;
}
}
int main(int argc, char** argv)
{
if (argc==1)
{
test_merge_duplicated_vertices_in_boundary_cycles("data/merge_points.off", 43);
}
else
{
for (int i=1; i< argc; ++i)
test_merge_duplicated_vertices_in_boundary_cycles(argv[i], 0);
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,250 @@
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/repair.h>
#include <CGAL/Polygon_mesh_processing/shape_predicates.h>
#include <boost/math/constants/constants.hpp>
#include <fstream>
#include <iostream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::FT FT;
typedef K::Point_3 Point_3;
typedef CGAL::Surface_mesh<Point_3> Surface_mesh;
void check_edge_degeneracy(const char* fname)
{
std::cout << "test edge degeneracy...";
typedef boost::graph_traits<Surface_mesh>::edge_descriptor edge_descriptor;
std::ifstream input(fname);
Surface_mesh mesh;
if (!input || !(input >> mesh) || mesh.is_empty()) {
std::cerr << fname << " is not a valid off file.\n";
std::exit(1);
}
std::vector<edge_descriptor> all_edges(edges(mesh).begin(), edges(mesh).end());
assert(!CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[0], mesh));
assert(!CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[1], mesh));
assert(CGAL::Polygon_mesh_processing::is_degenerate_edge(all_edges[2], mesh));
std::cout << "done" << std::endl;
}
void check_triangle_face_degeneracy(const char* fname)
{
std::cout << "test face degeneracy...";
typedef boost::graph_traits<Surface_mesh>::face_descriptor face_descriptor;
std::ifstream input(fname);
Surface_mesh mesh;
if (!input || !(input >> mesh) || mesh.is_empty()) {
std::cerr << fname << " is not a valid off file.\n";
std::exit(1);
}
std::vector<face_descriptor> all_faces(faces(mesh).begin(), faces(mesh).end());
assert(CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[0], mesh));
assert(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[1], mesh));
assert(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[2], mesh));
assert(!CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(all_faces[3], mesh));
std::cout << "done" << std::endl;
}
// tests merge_and_duplication
template <typename PolygonMesh>
void merge_identical_points(typename boost::graph_traits<PolygonMesh>::vertex_descriptor v_keep,
typename boost::graph_traits<PolygonMesh>::vertex_descriptor v_rm,
PolygonMesh& mesh)
{
typedef typename boost::graph_traits<PolygonMesh>::halfedge_descriptor halfedge_descriptor;
halfedge_descriptor h = halfedge(v_rm, mesh);
halfedge_descriptor start = h;
do
{
set_target(h, v_keep, mesh);
h = opposite(next(h, mesh), mesh);
}
while( h != start );
remove_vertex(v_rm, mesh);
}
void test_vertex_non_manifoldness(const char* fname)
{
std::cout << "test vertex non manifoldness...";
typedef boost::graph_traits<Surface_mesh>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Surface_mesh>::vertices_size_type size_type;
std::ifstream input(fname);
Surface_mesh mesh;
if (!input || !(input >> mesh) || mesh.is_empty()) {
std::cerr << fname << " is not a valid off file.\n";
std::exit(1);
}
size_type ini_nv = num_vertices(mesh);
// create non-manifold vertex
Surface_mesh::Vertex_index vertex_to_merge_onto(1);
Surface_mesh::Vertex_index vertex_to_merge(7);
merge_identical_points(vertex_to_merge_onto, vertex_to_merge, mesh);
mesh.collect_garbage();
assert(num_vertices(mesh) == ini_nv - 1);
BOOST_FOREACH(vertex_descriptor v, vertices(mesh))
{
if(v == vertex_to_merge_onto)
assert(CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh));
else
assert(!CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, mesh));
}
std::cout << "done" << std::endl;
}
void test_vertices_merge_and_duplication(const char* fname)
{
std::cout << "test non manifold vertex duplication...";
typedef boost::graph_traits<Surface_mesh>::vertex_descriptor vertex_descriptor;
std::ifstream input(fname);
Surface_mesh mesh;
if (!input || !(input >> mesh) || mesh.is_empty()) {
std::cerr << fname << " is not a valid off file.\n";
std::exit(1);
}
const std::size_t initial_vertices = num_vertices(mesh);
// create non-manifold vertex
Surface_mesh::Vertex_index vertex_to_merge_onto(1);
Surface_mesh::Vertex_index vertex_to_merge(7);
Surface_mesh::Vertex_index vertex_to_merge_2(14);
Surface_mesh::Vertex_index vertex_to_merge_3(21);
Surface_mesh::Vertex_index vertex_to_merge_onto_2(2);
Surface_mesh::Vertex_index vertex_to_merge_4(8);
merge_identical_points(vertex_to_merge_onto, vertex_to_merge, mesh);
merge_identical_points(vertex_to_merge_onto, vertex_to_merge_2, mesh);
merge_identical_points(vertex_to_merge_onto, vertex_to_merge_3, mesh);
merge_identical_points(vertex_to_merge_onto_2, vertex_to_merge_4, mesh);
mesh.collect_garbage();
const std::size_t vertices_after_merge = num_vertices(mesh);
assert(vertices_after_merge == initial_vertices - 4);
std::vector<std::vector<vertex_descriptor> > duplicated_vertices;
std::size_t new_vertices_nb =
CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices(mesh,
CGAL::parameters::output_iterator(std::back_inserter(duplicated_vertices)));
const std::size_t final_vertices_size = vertices(mesh).size();
assert(final_vertices_size == initial_vertices);
assert(new_vertices_nb == 4);
assert(duplicated_vertices.size() == 2); // two non-manifold vertex
assert(duplicated_vertices.front().size() == 4);
assert(duplicated_vertices.back().size() == 2);
std::cout << "done" << std::endl;
}
void test_needles_and_caps(const char* fname)
{
std::cout << "test needles&caps...";
namespace PMP = CGAL::Polygon_mesh_processing;
typedef boost::graph_traits<Surface_mesh>::halfedge_descriptor halfedge_descriptor;
typedef boost::graph_traits<Surface_mesh>::face_iterator face_iterator;
typedef boost::graph_traits<Surface_mesh>::face_descriptor face_descriptor;
std::ifstream input(fname);
Surface_mesh mesh;
if (!input || !(input >> mesh) || mesh.is_empty()) {
std::cerr << fname << " is not a valid off file.\n";
std::exit(1);
}
const FT eps = std::numeric_limits<FT>::epsilon();
face_iterator fit, fend;
boost::tie(fit, fend) = faces(mesh);
// (0 0 0) -- (1 0 0) -- (1 1 0) (90° cap angle)
face_descriptor f = *fit;
halfedge_descriptor res = PMP::is_needle_triangle_face(f, mesh, 2/*needle_threshold*/);
assert(res == boost::graph_traits<Surface_mesh>::null_halfedge()); // not a needle
res = PMP::is_needle_triangle_face(f, mesh, CGAL::sqrt(FT(2) - eps)/*needle_threshold*/);
assert(res != boost::graph_traits<Surface_mesh>::null_halfedge()); // is a needle
res = PMP::is_cap_triangle_face(f, mesh, 0./*cos(pi/2)*/);
assert(mesh.point(target(res, mesh)) == CGAL::ORIGIN);
res = PMP::is_cap_triangle_face(f, mesh, std::cos(91 * CGAL_PI / 180));
assert(res == boost::graph_traits<Surface_mesh>::null_halfedge()); res = PMP::is_cap_triangle_face(f, mesh, std::cos(boost::math::constants::two_thirds_pi<FT>()));
assert(res == boost::graph_traits<Surface_mesh>::null_halfedge());
++ fit;
// (0 0 1) -- (1 0 1) -- (10 10 1)
f = *fit;
res = PMP::is_needle_triangle_face(f, mesh, 20);
assert(res == boost::graph_traits<Surface_mesh>::null_halfedge());
res = PMP::is_needle_triangle_face(f, mesh, 10 * CGAL::sqrt(FT(2) - eps));
assert(mesh.point(target(res, mesh)) == Point_3(1,0,1));
res = PMP::is_needle_triangle_face(f, mesh, 1);
assert(mesh.point(target(res, mesh)) == Point_3(1,0,1));
res = PMP::is_cap_triangle_face(f, mesh, 0./*cos(pi/2)*/);
assert(mesh.point(target(res, mesh)) == Point_3(0,0,1));
res = PMP::is_cap_triangle_face(f, mesh, std::cos(boost::math::constants::two_thirds_pi<FT>()));
assert(mesh.point(target(res, mesh)) == Point_3(0,0,1));
res = PMP::is_cap_triangle_face(f, mesh, std::cos(boost::math::constants::three_quarters_pi<FT>()));
assert(res == boost::graph_traits<Surface_mesh>::null_halfedge());
++ fit;
// (0 0 2) -- (1 0 2) -- (-0.99619469809 0.08715574274 2) (175° cap angle)
f = *fit;
res = PMP::is_needle_triangle_face(f, mesh, 2);
assert(res == boost::graph_traits<Surface_mesh>::null_halfedge());
res = PMP::is_needle_triangle_face(f, mesh, 1.9);
assert(mesh.point(target(res, mesh)) == Point_3(0,0,2) ||
mesh.point(target(res, mesh)) == Point_3(1,0,2));
res = PMP::is_needle_triangle_face(f, mesh, 1);
assert(mesh.point(target(res, mesh)) == Point_3(0,0,2) ||
mesh.point(target(res, mesh)) == Point_3(1,0,2));
res = PMP::is_cap_triangle_face(f, mesh, 0./*cos(pi/2)*/);
assert(res != boost::graph_traits<Surface_mesh>::null_halfedge() &&
mesh.point(target(res, mesh)) != Point_3(0,0,2) &&
mesh.point(target(res, mesh)) != Point_3(1,0,2));
res = PMP::is_cap_triangle_face(f, mesh, std::cos(boost::math::constants::two_thirds_pi<FT>()));
assert(res != boost::graph_traits<Surface_mesh>::null_halfedge());
res = PMP::is_cap_triangle_face(f, mesh, std::cos(175 * CGAL_PI / 180));
assert(res != boost::graph_traits<Surface_mesh>::null_halfedge());
res = PMP::is_cap_triangle_face(f, mesh, std::cos(176 * CGAL_PI / 180));
assert(res == boost::graph_traits<Surface_mesh>::null_halfedge());
std::cout << "done" << std::endl;
}
int main()
{
check_edge_degeneracy("data_degeneracies/degtri_edge.off");
check_triangle_face_degeneracy("data_degeneracies/degtri_four.off");
test_vertex_non_manifoldness("data/blobby.off");
test_vertices_merge_and_duplication("data/blobby.off");
test_needles_and_caps("data_degeneracies/caps_and_needles.off");
return EXIT_SUCCESS;
}

View File

@ -19,6 +19,9 @@
#include <CGAL/box_intersection_d.h>
#include <CGAL/Make_triangle_soup.h>
#include <CGAL/Kernel_traits.h>
#include <CGAL/Polygon_mesh_processing/shape_predicates.h>
#ifdef USE_SURFACE_MESH
typedef Scene_surface_mesh_item Scene_facegraph_item;
#else
@ -80,14 +83,12 @@ template<class Mesh>
bool isDegen(Mesh* mesh, std::vector<typename boost::graph_traits<Mesh>::face_descriptor> &out_faces)
{
typedef typename boost::graph_traits<Mesh>::face_descriptor FaceDescriptor;
typedef typename boost::property_map<Mesh, CGAL::vertex_point_t>::type Vpm;
typedef typename boost::property_traits<Vpm>::value_type Point;
typedef typename CGAL::Kernel_traits<Point>::Kernel Kernel;
//filter non-triangle_faces
BOOST_FOREACH(FaceDescriptor f, faces(*mesh))
{
if(is_triangle(halfedge(f, *mesh), *mesh)
&& is_degenerate_triangle_face(f, *mesh, get(boost::vertex_point, *mesh), Kernel()) )
&& CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, *mesh) )
out_faces.push_back(f);
}
return !out_faces.empty();

View File

@ -58,7 +58,7 @@ public:
actionStitchCloseBorderHalfedges->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental");
actionRemoveSelfIntersections->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental");
actionRemoveIsolatedVertices->setProperty("subMenuName", "Polygon Mesh Processing/Repair");
actionDuplicateNMVertices->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental");
actionDuplicateNMVertices->setProperty("subMenuName", "Polygon Mesh Processing/Repair");
actionAutorefine->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental");
actionAutorefineAndRMSelfIntersections->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental");

View File

@ -27,6 +27,7 @@
#include <CGAL/boost/graph/split_graph_into_polylines.h>
#include <CGAL/Polygon_mesh_processing/border.h>
#include <CGAL/Polygon_mesh_processing/repair.h>
#include <CGAL/Polygon_mesh_processing/shape_predicates.h>
#include <Scene.h>
#ifdef USE_SURFACE_MESH
@ -741,14 +742,10 @@ public Q_SLOTS:
//Edition mode
case 1:
{
VPmap vpmap = get(CGAL::vertex_point, *selection_item->polyhedron());
bool is_valid = true;
BOOST_FOREACH(boost::graph_traits<Face_graph>::face_descriptor fd, faces(*selection_item->polyhedron()))
{
if (CGAL::is_degenerate_triangle_face(fd,
*selection_item->polyhedron(),
vpmap,
CGAL::Kernel_traits< boost::property_traits<VPmap>::value_type >::Kernel()))
if (CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(fd, *selection_item->polyhedron()))
{
is_valid = false;
break;

View File

@ -9,6 +9,7 @@
#include "Scene_edit_polyhedron_item.h"
#include "Scene_polyhedron_selection_item.h"
#include <CGAL/Polygon_mesh_processing/repair.h>
#include <CGAL/Polygon_mesh_processing/shape_predicates.h>
#include <QAction>
#include <QMainWindow>
#include <QFileDialog>
@ -421,10 +422,7 @@ void Polyhedron_demo_edit_polyhedron_plugin::dock_widget_visibility_changed(bool
bool is_valid = true;
BOOST_FOREACH(boost::graph_traits<Face_graph>::face_descriptor fd, faces(*poly_item->face_graph()))
{
if (CGAL::is_degenerate_triangle_face(fd,
*poly_item->face_graph(),
get(boost::vertex_point,
*poly_item->face_graph()), Kernel()))
if (CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(fd, *poly_item->face_graph()))
{
is_valid = false;
break;

View File

@ -15,6 +15,7 @@
#include <CGAL/Polygon_mesh_processing/connected_components.h>
#include <CGAL/Polygon_mesh_processing/measure.h>
#include <CGAL/Polygon_mesh_processing/self_intersections.h>
#include <CGAL/Polygon_mesh_processing/shape_predicates.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
#include <CGAL/Polygon_mesh_processing/orient_polygon_soup.h>
#include <CGAL/boost/graph/selection.h>
@ -287,7 +288,7 @@ void* Scene_polyhedron_item_priv::get_aabb_tree()
int index =0;
BOOST_FOREACH( Polyhedron::Facet_iterator f, faces(*poly))
{
if (CGAL::is_degenerate_triangle_face(f, *poly, get(CGAL::vertex_point, *poly), Kernel()))
if (CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, *poly))
continue;
if(!f->is_triangle())
{
@ -1677,7 +1678,7 @@ QString Scene_polyhedron_item::computeStats(int type)
if (d->poly->is_pure_triangle())
{
if (d->number_of_degenerated_faces == (unsigned int)(-1))
d->number_of_degenerated_faces = nb_degenerate_faces(d->poly, get(CGAL::vertex_point, *(d->poly)));
d->number_of_degenerated_faces = nb_degenerate_faces(d->poly);
return QString::number(d->number_of_degenerated_faces);
}
else

View File

@ -2,6 +2,7 @@
#include "Scene_polyhedron_selection_item.h"
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
#include <CGAL/Polygon_mesh_processing/repair.h>
#include <CGAL/Polygon_mesh_processing/shape_predicates.h>
#include <CGAL/boost/graph/dijkstra_shortest_paths.h>
#include <CGAL/boost/graph/helpers.h>
#include <CGAL/property_map.h>
@ -2076,8 +2077,9 @@ bool Scene_polyhedron_selection_item_priv::canAddFace(fg_halfedge_descriptor hc,
found = true;
fg_halfedge_descriptor res =
CGAL::Euler::add_face_to_border(t,hc, *item->polyhedron());
fg_face_descriptor resf = face(res, *item->polyhedron());
if(CGAL::is_degenerate_triangle_face(res, *item->polyhedron(), get(CGAL::vertex_point, *item->polyhedron()), Kernel()))
if(CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(resf, *item->polyhedron()))
{
CGAL::Euler::remove_face(res, *item->polyhedron());
tempInstructions("Edge not selected : resulting facet is degenerated.",

View File

@ -28,6 +28,7 @@
#include <CGAL/Polygon_mesh_processing/connected_components.h>
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
#include <CGAL/Polygon_mesh_processing/self_intersections.h>
#include <CGAL/Polygon_mesh_processing/shape_predicates.h>
#include <CGAL/Polygon_mesh_processing/orient_polygon_soup.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
#include "triangulate_primitive.h"
@ -1045,7 +1046,7 @@ void* Scene_surface_mesh_item_priv::get_aabb_tree()
BOOST_FOREACH( face_descriptor f, faces(*sm))
{
//if face is degenerate, skip it
if (CGAL::is_degenerate_triangle_face(f, *sm, get(CGAL::vertex_point, *sm), EPICK()))
if (CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(f, *sm))
continue;
//if face not triangle, triangulate corresponding primitive before adding it to the tree
if(!CGAL::is_triangle(halfedge(f, *sm), *sm))
@ -1537,7 +1538,7 @@ QString Scene_surface_mesh_item::computeStats(int type)
if(is_triangle_mesh(*d->smesh_))
{
if (d->number_of_degenerated_faces == (unsigned int)(-1))
d->number_of_degenerated_faces = nb_degenerate_faces(d->smesh_, get(CGAL::vertex_point, *(d->smesh_)));
d->number_of_degenerated_faces = nb_degenerate_faces(d->smesh_);
return QString::number(d->number_of_degenerated_faces);
}
else

View File

@ -1,7 +1,8 @@
#ifndef POLYHEDRON_DEMO_STATISTICS_HELPERS_H
#define POLYHEDRON_DEMO_STATISTICS_HELPERS_H
#include <cmath>
#include <CGAL/squared_distance_3_0.h>
#include <CGAL/Polygon_mesh_processing/repair.h>
#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics/stats.hpp>
@ -9,10 +10,13 @@
#include <boost/accumulators/statistics/min.hpp>
#include <boost/accumulators/statistics/max.hpp>
#include <boost/accumulators/statistics/median.hpp>
#include <CGAL/squared_distance_3_0.h>
#include <map>
#include <boost/property_map/property_map.hpp>
#include <cmath>
#include <iterator>
#include <limits>
#include <map>
#include <vector>
template<typename Mesh>
void angles(Mesh* poly, double& mini, double& maxi, double& ave)
@ -81,19 +85,15 @@ void edges_length(Mesh* poly,
mid = extract_result< tag::median >(acc);
}
template<typename Mesh, typename VPmap>
unsigned int nb_degenerate_faces(Mesh* poly, VPmap vpmap)
template<typename Mesh>
unsigned int nb_degenerate_faces(Mesh* poly)
{
typedef typename boost::graph_traits<Mesh>::face_descriptor face_descriptor;
typedef typename CGAL::Kernel_traits< typename boost::property_traits<VPmap>::value_type >::Kernel Traits;
unsigned int nb = 0;
BOOST_FOREACH(face_descriptor f, faces(*poly))
{
if (CGAL::is_degenerate_triangle_face(f, *poly, vpmap, Traits()))
++nb;
}
return nb;
std::vector<face_descriptor> degenerate_faces;
CGAL::Polygon_mesh_processing::degenerate_faces(*poly, std::back_inserter(degenerate_faces));
return static_cast<unsigned int>(degenerate_faces.size());
}
template<typename Mesh>

View File

@ -462,10 +462,11 @@ make_property_map(const std::vector<T>& v)
template<class KeyType, class ValueType>
struct Constant_property_map
{
const ValueType default_value;
ValueType default_value;
typedef KeyType key_type;
typedef ValueType value_type;
typedef value_type& reference;
typedef boost::read_write_property_map_tag category;
Constant_property_map(const value_type& default_value = value_type()) : default_value (default_value) { }