mirror of https://github.com/CGAL/cgal
Merge branch 'repair_functions-old' into repair_functions
This commit is contained in:
commit
1676cd7405
|
|
@ -38,16 +38,16 @@ num_edges(const EdgeListGraph& g);
|
||||||
|
|
||||||
|
|
||||||
/*! \relates EdgeListGraph
|
/*! \relates EdgeListGraph
|
||||||
returns the source vertex of `h`.
|
returns the source vertex of `e`.
|
||||||
*/
|
*/
|
||||||
template <typename EdgeListGraph>
|
template <typename EdgeListGraph>
|
||||||
boost::graph_traits<EdgeListGraph>::vertex_descriptor
|
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
|
/*! \relates EdgeListGraph
|
||||||
returns the target vertex of `h`.
|
returns the target vertex of `e`.
|
||||||
*/
|
*/
|
||||||
template <typename EdgeListGraph>
|
template <typename EdgeListGraph>
|
||||||
boost::graph_traits<EdgeListGraph>::vertex_descriptor
|
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);
|
||||||
|
|
|
||||||
|
|
@ -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>
|
<td>An upper bound of the number of edges of the graph</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>`source(g)`</td>
|
<td>`source(e, g)`</td>
|
||||||
<td>`vertex_descriptor`</td>
|
<td>`vertex_descriptor`</td>
|
||||||
<td>The source vertex of `e`</td>
|
<td>The source vertex of `e`</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>`target(g)`</td>
|
<td>`target(e, g)`</td>
|
||||||
<td>`vertex_descriptor`</td>
|
<td>`vertex_descriptor`</td>
|
||||||
<td>The target vertex of `e`</td>
|
<td>The target vertex of `e`</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
||||||
|
|
@ -973,33 +973,6 @@ make_tetrahedron(const P& p0, const P& p1, const P& p2, const P& p3, Graph& g)
|
||||||
return opposite(h2,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
|
* \ingroup PkgBGLHelperFct
|
||||||
* \brief Creates a triangulated regular prism, outward oriented,
|
* \brief Creates a triangulated regular prism, outward oriented,
|
||||||
|
|
|
||||||
|
|
@ -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(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(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(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'
|
// 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)
|
CGAL_add_named_parameter(get_cost_policy_t, get_cost_policy, get_cost)
|
||||||
|
|
|
||||||
|
|
@ -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::verbosity_level).v == 41);
|
||||||
assert(get_param(np, CGAL::internal_np::projection_functor).v == 42);
|
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::apply_per_connected_component).v == 46);
|
||||||
|
assert(get_param(np, CGAL::internal_np::output_iterator).v == 47);
|
||||||
|
|
||||||
|
|
||||||
// Test types
|
// 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<41>(get_param(np, CGAL::internal_np::verbosity_level));
|
||||||
check_same_type<42>(get_param(np, CGAL::internal_np::projection_functor));
|
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<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()
|
int main()
|
||||||
|
|
@ -238,6 +240,7 @@ int main()
|
||||||
.clip_volume(A<44>(44))
|
.clip_volume(A<44>(44))
|
||||||
.use_compact_clipper(A<45>(45))
|
.use_compact_clipper(A<45>(45))
|
||||||
.apply_per_connected_component(A<46>(46))
|
.apply_per_connected_component(A<46>(46))
|
||||||
|
.output_iterator(A<47>(47))
|
||||||
);
|
);
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,25 @@
|
||||||
Release History
|
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
|
Release 4.13
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -722,6 +722,8 @@ public:
|
||||||
\cgalHasModel `CGAL::Vector_2<Kernel>`
|
\cgalHasModel `CGAL::Vector_2<Kernel>`
|
||||||
|
|
||||||
\sa `Kernel::ComputeDeterminant_2`
|
\sa `Kernel::ComputeDeterminant_2`
|
||||||
|
\sa `Kernel::ComputeScalarProduct_2`
|
||||||
|
\sa `Kernel::ComputeSquaredLength_2`
|
||||||
\sa `Kernel::ComputeX_2`
|
\sa `Kernel::ComputeX_2`
|
||||||
\sa `Kernel::ComputeY_2`
|
\sa `Kernel::ComputeY_2`
|
||||||
\sa `Kernel::ComputeHx_2`
|
\sa `Kernel::ComputeHx_2`
|
||||||
|
|
@ -754,7 +756,10 @@ A type representing vectors in three dimensions.
|
||||||
|
|
||||||
\cgalHasModel `CGAL::Vector_3<Kernel>`
|
\cgalHasModel `CGAL::Vector_3<Kernel>`
|
||||||
|
|
||||||
\sa `Kernel::ComputeDeterminant_3`
|
\sa `Kernel::CompareDihedralAngle_3`
|
||||||
|
\sa `Kernel::ComputeDeterminant_3`
|
||||||
|
\sa `Kernel::ComputeScalarProduct_3`
|
||||||
|
\sa `Kernel::ComputeSquaredLength_3`
|
||||||
\sa `Kernel::ComputeX_3`
|
\sa `Kernel::ComputeX_3`
|
||||||
\sa `Kernel::ComputeY_3`
|
\sa `Kernel::ComputeY_3`
|
||||||
\sa `Kernel::ComputeZ_3`
|
\sa `Kernel::ComputeZ_3`
|
||||||
|
|
|
||||||
|
|
@ -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.
|
Parameter used to pass a visitor class to a function. Its type and behavior depend on the visited function.
|
||||||
\n
|
\n
|
||||||
\b Type : `A class` \n
|
\b Type : `A class` \n
|
||||||
\b Default Specific to the function visited
|
\b Default : Specific to the function visited
|
||||||
\cgalNPEnd
|
\cgalNPEnd
|
||||||
|
|
||||||
\cgalNPBegin{throw_on_self_intersection} \anchor PMP_throw_on_self_intersection
|
\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`
|
\b Default value is `true`
|
||||||
\cgalNPEnd
|
\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
|
\cgalNPTableEnd
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -113,19 +113,29 @@ and provides a list of the parameters that are used in this package.
|
||||||
- `CGAL::Polygon_mesh_processing::self_intersections()`
|
- `CGAL::Polygon_mesh_processing::self_intersections()`
|
||||||
- \link PMP_predicates_grp `CGAL::Polygon_mesh_processing::do_intersect()` \endlink
|
- \link PMP_predicates_grp `CGAL::Polygon_mesh_processing::do_intersect()` \endlink
|
||||||
- `CGAL::Polygon_mesh_processing::intersecting_meshes()`
|
- `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 ##
|
## 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_polygon_soup()`
|
||||||
- `CGAL::Polygon_mesh_processing::orient()`
|
- `CGAL::Polygon_mesh_processing::orient()`
|
||||||
- `CGAL::Polygon_mesh_processing::orient_to_bound_a_volume()`
|
- `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 ##
|
## Combinatorial Repairing Functions ##
|
||||||
- \link PMP_repairing_grp `CGAL::Polygon_mesh_processing::stitch_borders()` \endlink
|
- \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::is_polygon_soup_a_polygon_mesh()`
|
||||||
- `CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh()`
|
- `CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh()`
|
||||||
- `CGAL::Polygon_mesh_processing::remove_isolated_vertices()`
|
- `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 ##
|
## Normal Computation Functions ##
|
||||||
- `CGAL::Polygon_mesh_processing::compute_face_normal()`
|
- `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::edge_bbox()`
|
||||||
- `CGAL::Polygon_mesh_processing::face_bbox()`
|
- `CGAL::Polygon_mesh_processing::face_bbox()`
|
||||||
- `CGAL::Polygon_mesh_processing::border_halfedges()`
|
- `CGAL::Polygon_mesh_processing::border_halfedges()`
|
||||||
|
- `CGAL::Polygon_mesh_processing::extract_boundary_cycles()`
|
||||||
- `CGAL::Polygon_mesh_processing::transform()`
|
- `CGAL::Polygon_mesh_processing::transform()`
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -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
|
- \ref Coref_section : methods to corefine triangle meshes and to compute
|
||||||
boolean operations out of corefined closed triangle meshes.
|
boolean operations out of corefined closed triangle meshes.
|
||||||
- \ref PMPHoleFilling : available hole filling algorithms, which can possibly be combined with refinement and fairing.
|
- \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.
|
mesh, which includes point location and self intersection tests.
|
||||||
- \ref PMPOrientation : checking or fixing the \ref PMPOrientation of a polygon soup.
|
- \ref PMPOrientation : checking or fixing the orientation of a polygon soup.
|
||||||
- \ref PMPRepairing : reparation of polygon meshes and polygon soups.
|
- \ref PMPRepairing : repair of polygon meshes and polygon soups.
|
||||||
- \ref PMPNormalComp : normal computation at vertices and on faces of a polygon mesh.
|
- \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 PMPSlicer : functor able to compute the intersections of a polygon mesh with arbitrary planes (slicer).
|
||||||
- \ref PMPConnectedComponents : methods to deal with connected
|
- \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
|
\section PMPMeshing Meshing
|
||||||
|
|
@ -309,7 +309,7 @@ This package provides an algorithm for filling one closed hole that is either in
|
||||||
or defined by a sequence of points that describe a polyline.
|
or defined by a sequence of points that describe a polyline.
|
||||||
The main steps of the algorithm are described in \cgalCite{liepa2003filling} and can be summarized as follows.
|
The main steps of the algorithm are described in \cgalCite{liepa2003filling} and can be summarized as follows.
|
||||||
|
|
||||||
First, the largest patch triangulating the boundary of the hole is generated without introducing any new vertex.
|
First, the largest patch triangulating the boundary of the hole is generated without introducing any new vertex.
|
||||||
The patch is selected so as to minimize a quality function evaluated for all possible triangular patches.
|
The patch is selected so as to minimize a quality function evaluated for all possible triangular patches.
|
||||||
The quality function first minimizes the worst dihedral angle between patch triangles,
|
The quality function first minimizes the worst dihedral angle between patch triangles,
|
||||||
then the total surface area of the patch as a tiebreaker.
|
then the total surface area of the patch as a tiebreaker.
|
||||||
|
|
@ -335,10 +335,10 @@ From left to right: (a) the hole,
|
||||||
\subsection HoleFillingAPI API
|
\subsection HoleFillingAPI API
|
||||||
|
|
||||||
This package provides four functions for hole filling:
|
This package provides four functions for hole filling:
|
||||||
- `triangulate_hole_polyline()` : given a sequence of points defining the hole, triangulates the hole.
|
- `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_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_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_refine_and_fair_hole()` : in addition to `triangulate_and_refine_hole()` the generated patch is also faired.
|
||||||
|
|
||||||
\subsection HFExamples Examples
|
\subsection HFExamples Examples
|
||||||
|
|
||||||
|
|
@ -372,51 +372,65 @@ iteratively filled, refined and faired to get a faired mesh with no hole.
|
||||||
The hole filling algorithm has a complexity which depends on the
|
The hole filling algorithm has a complexity which depends on the
|
||||||
number of vertices. While \cgalCite{liepa2003filling} has a running
|
number of vertices. While \cgalCite{liepa2003filling} has a running
|
||||||
time of \f$ O(n^3)\f$ , \cgalCite{zou2013algorithm} in most cases has
|
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
|
running time of \f$ O(n \log n)\f$. We benchmarked the function
|
||||||
`triangulate_refine_and_fair_hole()` for the below meshes (and two
|
`triangulate_refine_and_fair_hole()` for the two meshes below (as well as two
|
||||||
more meshes with smaller holes). The machine used is a PC running
|
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.
|
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
|
The program was compiled with the Visual C++ 2013 compiler with the O2
|
||||||
option which maximizes speed.
|
option, which maximizes speed.
|
||||||
|
|
||||||
\cgalFigureBegin{Elephants, elephants-with-holes.png}
|
\cgalFigureBegin{Elephants, elephants-with-holes.png}
|
||||||
The elephant on the left/right has a hole with 963/7657 vertices.
|
The elephant on the left/right has a hole with 963/7657 vertices.
|
||||||
\cgalFigureEnd
|
\cgalFigureEnd
|
||||||
|
|
||||||
This takes time
|
The following running times were observed:
|
||||||
|
|
||||||
|
<center>
|
||||||
| # vertices | without Delaunay (sec.) | with Delaunay (sec.)|
|
| # vertices | without Delaunay (sec.) | with Delaunay (sec.)|
|
||||||
| ----: | ----: | ----: |
|
| ----: | ----: | ----: |
|
||||||
565 | 8.5 | 0.03 |
|
565 | 8.5 | 0.03 |
|
||||||
774 | 21 | 0.035 |
|
774 | 21 | 0.035 |
|
||||||
967 | 43 | 0.06 |
|
967 | 43 | 0.06 |
|
||||||
7657 | na | 0.4 |
|
7657 | na | 0.4 |
|
||||||
|
</center>
|
||||||
|
|
||||||
***************************************
|
***************************************
|
||||||
\section PMPPredicates Predicates
|
\section PMPPredicates Predicates
|
||||||
|
|
||||||
This packages provides several predicates to be evaluated with respect to a triangle mesh.
|
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()`.
|
`CGAL::Polygon_mesh_processing::does_self_intersect()`.
|
||||||
Additionally, the function `CGAL::Polygon_mesh_processing::self_intersections()`
|
Additionally, the function `CGAL::Polygon_mesh_processing::self_intersections()`
|
||||||
reports all pairs of intersecting triangles.
|
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
|
\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}
|
\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
|
\subsection PMPInsideTest Side of Triangle Mesh
|
||||||
|
|
||||||
The class `CGAL::Side_of_triangle_mesh` provides a functor that tests whether a query point is
|
The class `CGAL::Side_of_triangle_mesh` provides a functor that tests whether a query point is
|
||||||
inside, outside, or on the boundary of the domain bounded by a given closed triangle mesh.
|
inside, outside, or on the boundary of the domain bounded by a given closed triangle mesh.
|
||||||
|
|
||||||
A point is said to be on the bounded side of the domain bounded by the input triangle mesh
|
A point is said to be on the bounded side of the domain bounded by the input triangle mesh
|
||||||
|
|
@ -435,39 +449,88 @@ input triangle mesh.
|
||||||
\subsubsection InsideExample Inside Test Example
|
\subsubsection InsideExample Inside Test Example
|
||||||
\cgalExample{Polygon_mesh_processing/point_inside_example.cpp}
|
\cgalExample{Polygon_mesh_processing/point_inside_example.cpp}
|
||||||
|
|
||||||
\subsection PMPDoIntersect Intersections Detection
|
\subsection PMPShapePredicates Shape Predicates
|
||||||
Intersection tests between triangle meshes and/or polylines can be done using
|
|
||||||
\link PMP_predicates_grp `CGAL::Polygon_mesh_processing::do_intersect()` \endlink.
|
Badly shaped or, even worse, completely degenerate elements of a polygon mesh are problematic
|
||||||
Additionally, the function `CGAL::Polygon_mesh_processing::intersecting_meshes()`
|
in many algorithms which one might want to use on the mesh.
|
||||||
records all pairs of intersecting meshes in a range.
|
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
|
\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.
|
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
|
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.
|
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.
|
of halfedges around faces.
|
||||||
As a consequence, the normal computed for each face (see Section
|
As a consequence, the normal computed for each face (see Section
|
||||||
\ref PMPNormalComp) is also reversed.
|
\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
|
This example shows how to generate a mesh from a polygon soup.
|
||||||
of a closed polygon mesh outward or inward oriented.
|
The first step is to get a soup of consistently oriented faces, before
|
||||||
|
rebuilding the connectivity.
|
||||||
The function `CGAL::Polygon_mesh_processing::orient_to_bound_a_volume()` orients
|
In this example, some orientation tests are performed on the output
|
||||||
the connected components of a closed polygon mesh so that it bounds a volume
|
polygon mesh to illustrate
|
||||||
(see \ref coref_def_subsec for the precise definition).
|
Section \ref PMPOrientation.
|
||||||
|
|
||||||
|
\cgalExample{Polygon_mesh_processing/orient_polygon_soup_example.cpp}
|
||||||
|
|
||||||
****************************************
|
****************************************
|
||||||
\section PMPRepairing Combinatorial Repairing
|
\section PMPRepairing Combinatorial Repairing
|
||||||
*******************
|
*******************
|
||||||
\subsection Stitching
|
\subsection Stitching
|
||||||
|
|
||||||
|
|
@ -490,16 +553,14 @@ with duplicated border edges.
|
||||||
|
|
||||||
\cgalExample{Polygon_mesh_processing/stitch_borders_example.cpp}
|
\cgalExample{Polygon_mesh_processing/stitch_borders_example.cpp}
|
||||||
|
|
||||||
*******************
|
|
||||||
\if READY_TO_PUBLISH
|
\if READY_TO_PUBLISH
|
||||||
|
|
||||||
\subsection DegenerateFaces Removing Degenerate Faces
|
\subsection DegenerateFaces Removing Degenerate Faces
|
||||||
|
|
||||||
Some degenerate faces may be part of a given triangle mesh.
|
Some degenerate faces may be part of a given triangle mesh.
|
||||||
A face is considered \e degenerate if two of its vertices
|
A face is considered \e degenerate if two of its vertices
|
||||||
share the same location,
|
share the same location, or more generally if its three vertices are collinear.
|
||||||
or in general if its three vertices are collinear.
|
The function `CGAL::Polygon_mesh_processing::remove_degenerate_faces()`
|
||||||
The function
|
|
||||||
`CGAL::Polygon_mesh_processing::remove_degenerate_faces()`
|
|
||||||
removes those faces and fixes the connectivity of the newly cleaned up mesh.
|
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
|
It is also possible to remove isolated vertices from any polygon mesh, using the function
|
||||||
`CGAL::Polygon_mesh_processing::remove_isolated_vertices()`.
|
`CGAL::Polygon_mesh_processing::remove_isolated_vertices()`.
|
||||||
|
|
@ -512,52 +573,35 @@ is output.
|
||||||
|
|
||||||
\cgalExample{Polygon_mesh_processing/remove_degeneracies_example.cpp}
|
\cgalExample{Polygon_mesh_processing/remove_degeneracies_example.cpp}
|
||||||
\endif
|
\endif
|
||||||
*******************
|
|
||||||
\subsection PolygonSoups Polygon Soups
|
|
||||||
|
|
||||||
When the faces of a polygon mesh are given but the connectivity is unknown,
|
\endcond
|
||||||
we must deal with of a \e polygon \e soup.
|
|
||||||
|
|
||||||
Before running any of the algorithms on the so-called
|
\subsection PMPManifoldness Polygon Mesh Manifoldness
|
||||||
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
|
Non-manifold vertices can be detected using the function `CGAL::Polygon_mesh_processing::is_non_manifold_vertex()`.
|
||||||
combinatorial manifold surface, some points are duplicated.
|
The function `CGAL::Polygon_mesh_processing::duplicate_non_manifold_vertices()` can be used
|
||||||
Because a polygon soup does not have any connectivity (each point
|
to attempt to create a combinatorially manifold surface mesh by splitting any non-manifold vertex
|
||||||
has as many occurrences as the number of polygons it belongs to),
|
into as many vertices as there are manifold sheets at this geometric position.
|
||||||
duplicating one point (or a pair of points)
|
Note however that the mesh will still not be manifold from a geometric
|
||||||
amounts to duplicate the polygon to which it belongs.
|
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
|
\subsubsection FixNMVerticeExample Manifoldness Repair Example
|
||||||
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,
|
In the following example, a non-manifold configuration is artifically created and
|
||||||
with possibly duplicated (or more) points,
|
fixed with the help of the functions described above.
|
||||||
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.
|
|
||||||
|
|
||||||
|
\cgalExample{Polygon_mesh_processing/manifoldness_repair_example.cpp}
|
||||||
|
|
||||||
\subsubsection PolygonSoupExample Polygon Soup Example
|
\subsection PMPDuplicateVertexBoundaryCycle Duplicated Vertices in Boundary Cycles
|
||||||
|
|
||||||
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}
|
|
||||||
|
|
||||||
|
|
||||||
|
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
|
\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_face_normal()`
|
||||||
- `CGAL::Polygon_mesh_processing::compute_vertex_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 :
|
or to vertices, or to both :
|
||||||
- `CGAL::Polygon_mesh_processing::compute_face_normals()`
|
- `CGAL::Polygon_mesh_processing::compute_face_normals()`
|
||||||
- `CGAL::Polygon_mesh_processing::compute_vertex_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.
|
Property maps are used to record the computed normals.
|
||||||
|
|
||||||
|
|
||||||
\subsection NormalsExample Normals Computation Examples
|
\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
|
associate values to keys. In the following examples we associate
|
||||||
a normal vector to each vertex and to each face.
|
a normal vector to each vertex and to each face.
|
||||||
|
|
||||||
|
|
||||||
\subsubsection NormalsExampleSM Normals Computation for a Surface Mesh
|
\subsubsection NormalsExampleSM Normals Computation for a Surface Mesh
|
||||||
|
|
||||||
The following example illustrates how to
|
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
|
\subsection DetectFeaturesExample Feature Detection Example
|
||||||
In the following example, we count how many edges of `pmesh` are incident to two faces
|
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.
|
and the number of surface patches that are separated by these edges.
|
||||||
|
|
||||||
\cgalExample{Polygon_mesh_processing/detect_features_example.cpp}
|
\cgalExample{Polygon_mesh_processing/detect_features_example.cpp}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
\example Polygon_mesh_processing/triangulate_faces_example.cpp
|
\example Polygon_mesh_processing/triangulate_faces_example.cpp
|
||||||
\example Polygon_mesh_processing/connected_components_example.cpp
|
\example Polygon_mesh_processing/connected_components_example.cpp
|
||||||
\example Polygon_mesh_processing/face_filtered_graph_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/triangulate_polyline_example.cpp
|
||||||
\example Polygon_mesh_processing/refine_fair_example.cpp
|
\example Polygon_mesh_processing/refine_fair_example.cpp
|
||||||
\example Polygon_mesh_processing/mesh_slicer_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_mesh_union_and_intersection.cpp
|
||||||
\example Polygon_mesh_processing/corefinement_consecutive_bool_op.cpp
|
\example Polygon_mesh_processing/corefinement_consecutive_bool_op.cpp
|
||||||
\example Polygon_mesh_processing/detect_features_example.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 |
|
|
@ -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( "triangulate_faces_example.cpp")
|
||||||
create_single_source_cgal_program( "connected_components_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( "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( "triangulate_polyline_example.cpp")
|
||||||
create_single_source_cgal_program( "mesh_slicer_example.cpp")
|
create_single_source_cgal_program( "mesh_slicer_example.cpp")
|
||||||
#create_single_source_cgal_program( "remove_degeneracies_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( "corefinement_LCC.cpp")
|
||||||
create_single_source_cgal_program( "hole_filling_example_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( "detect_features_example.cpp" )
|
||||||
|
create_single_source_cgal_program( "manifoldness_repair_example.cpp" )
|
||||||
|
|
||||||
if(OpenMesh_FOUND)
|
if(OpenMesh_FOUND)
|
||||||
create_single_source_cgal_program( "compute_normals_example_OM.cpp" )
|
create_single_source_cgal_program( "compute_normals_example_OM.cpp" )
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -1,34 +1,33 @@
|
||||||
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
|
||||||
|
|
||||||
#include <CGAL/Polyhedron_3.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/orient_polygon_soup.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
|
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/orientation.h>
|
#include <CGAL/Polygon_mesh_processing/orientation.h>
|
||||||
|
|
||||||
|
#include <CGAL/IO/OFF_reader.h>
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
|
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
|
||||||
typedef CGAL::Polyhedron_3<K> Polyhedron;
|
typedef CGAL::Polyhedron_3<K, CGAL::Polyhedron_items_with_id_3> Polyhedron;
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
const char* filename = (argc > 1) ? argv[1] : "data/tet-shuffled.off";
|
const char* filename = (argc > 1) ? argv[1] : "data/tet-shuffled.off";
|
||||||
std::ifstream input(filename);
|
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;
|
std::cerr << "Cannot open file " << std::endl;
|
||||||
return 1;
|
return EXIT_FAILURE;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CGAL::Polygon_mesh_processing::orient_polygon_soup(points, polygons);
|
CGAL::Polygon_mesh_processing::orient_polygon_soup(points, polygons);
|
||||||
|
|
@ -36,8 +35,13 @@ int main(int argc, char* argv[])
|
||||||
Polyhedron mesh;
|
Polyhedron mesh;
|
||||||
CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh(points, polygons, 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)))
|
// Number the faces because 'orient_to_bound_a_volume' needs a face <--> index map
|
||||||
CGAL::Polygon_mesh_processing::reverse_face_orientations(mesh);
|
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");
|
std::ofstream out("tet-oriented1.off");
|
||||||
out << mesh;
|
out << mesh;
|
||||||
|
|
@ -48,5 +52,5 @@ int main(int argc, char* argv[])
|
||||||
out2 << mesh;
|
out2 << mesh;
|
||||||
out2.close();
|
out2.close();
|
||||||
|
|
||||||
return 0;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
@ -24,16 +24,16 @@
|
||||||
|
|
||||||
#include <CGAL/license/Polygon_mesh_processing/miscellaneous.h>
|
#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/graph/graph_traits.hpp>
|
||||||
#include <boost/foreach.hpp>
|
#include <boost/foreach.hpp>
|
||||||
#include <boost/type_traits/is_same.hpp>
|
#include <boost/type_traits/is_same.hpp>
|
||||||
#include <boost/unordered_set.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>
|
#include <set>
|
||||||
|
|
||||||
namespace CGAL{
|
namespace CGAL{
|
||||||
|
|
@ -257,6 +257,38 @@ namespace Polygon_mesh_processing {
|
||||||
|
|
||||||
return border_counter;
|
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 Polygon_mesh_processing
|
||||||
} // end of namespace CGAL
|
} // end of namespace CGAL
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@
|
||||||
#include <CGAL/Polygon_mesh_processing/repair.h>
|
#include <CGAL/Polygon_mesh_processing/repair.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/measure.h>
|
#include <CGAL/Polygon_mesh_processing/measure.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/connected_components.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_tree.h>
|
||||||
#include <CGAL/AABB_traits.h>
|
#include <CGAL/AABB_traits.h>
|
||||||
|
|
@ -90,21 +91,6 @@ namespace internal {
|
||||||
};
|
};
|
||||||
|
|
||||||
// A property map
|
// 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>
|
template <typename PM, typename FaceIndexMap>
|
||||||
struct Border_constraint_pmap
|
struct Border_constraint_pmap
|
||||||
{
|
{
|
||||||
|
|
@ -325,6 +311,7 @@ namespace internal {
|
||||||
public:
|
public:
|
||||||
Incremental_remesher(PolygonMesh& pmesh
|
Incremental_remesher(PolygonMesh& pmesh
|
||||||
, VertexPointMap& vpmap
|
, VertexPointMap& vpmap
|
||||||
|
, const GeomTraits& gt
|
||||||
, const bool protect_constraints
|
, const bool protect_constraints
|
||||||
, EdgeIsConstrainedMap ecmap
|
, EdgeIsConstrainedMap ecmap
|
||||||
, VertexIsConstrainedMap vcmap
|
, VertexIsConstrainedMap vcmap
|
||||||
|
|
@ -333,6 +320,7 @@ namespace internal {
|
||||||
, const bool build_tree = true)//built by the remesher
|
, const bool build_tree = true)//built by the remesher
|
||||||
: mesh_(pmesh)
|
: mesh_(pmesh)
|
||||||
, vpmap_(vpmap)
|
, vpmap_(vpmap)
|
||||||
|
, gt_(gt)
|
||||||
, build_tree_(build_tree)
|
, build_tree_(build_tree)
|
||||||
, has_border_(false)
|
, has_border_(false)
|
||||||
, input_triangles_()
|
, input_triangles_()
|
||||||
|
|
@ -364,9 +352,10 @@ namespace internal {
|
||||||
|
|
||||||
BOOST_FOREACH(face_descriptor f, face_range)
|
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;
|
continue;
|
||||||
}
|
|
||||||
Patch_id pid = get_patch_id(f);
|
Patch_id pid = get_patch_id(f);
|
||||||
input_triangles_.push_back(triangle(f));
|
input_triangles_.push_back(triangle(f));
|
||||||
input_patch_ids_.push_back(pid);
|
input_patch_ids_.push_back(pid);
|
||||||
|
|
@ -828,7 +817,8 @@ namespace internal {
|
||||||
debug_status_map();
|
debug_status_map();
|
||||||
debug_self_intersections();
|
debug_self_intersections();
|
||||||
CGAL_assertion(0 == PMP::remove_degenerate_faces(mesh_,
|
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
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -933,7 +923,7 @@ namespace internal {
|
||||||
debug_status_map();
|
debug_status_map();
|
||||||
CGAL_assertion(0 == PMP::remove_degenerate_faces(mesh_
|
CGAL_assertion(0 == PMP::remove_degenerate_faces(mesh_
|
||||||
, PMP::parameters::vertex_point_map(vpmap_)
|
, PMP::parameters::vertex_point_map(vpmap_)
|
||||||
.geom_traits(GeomTraits())));
|
.geom_traits(gt_)));
|
||||||
debug_self_intersections();
|
debug_self_intersections();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
@ -975,9 +965,9 @@ namespace internal {
|
||||||
|
|
||||||
else if (is_on_patch(v))
|
else if (is_on_patch(v))
|
||||||
{
|
{
|
||||||
Vector_3 vn = PMP::compute_vertex_normal(v, mesh_
|
Vector_3 vn = PMP::compute_vertex_normal(v, mesh_,
|
||||||
, PMP::parameters::vertex_point_map(vpmap_)
|
parameters::vertex_point_map(vpmap_)
|
||||||
.geom_traits(GeomTraits()));
|
.geom_traits(gt_));
|
||||||
put(propmap_normals, v, vn);
|
put(propmap_normals, v, vn);
|
||||||
|
|
||||||
Vector_3 move = CGAL::NULL_VECTOR;
|
Vector_3 move = CGAL::NULL_VECTOR;
|
||||||
|
|
@ -1469,20 +1459,8 @@ private:
|
||||||
if (f == boost::graph_traits<PM>::null_face())
|
if (f == boost::graph_traits<PM>::null_face())
|
||||||
return CGAL::NULL_VECTOR;
|
return CGAL::NULL_VECTOR;
|
||||||
|
|
||||||
halfedge_descriptor hd = halfedge(f, mesh_);
|
return PMP::compute_face_normal(f, mesh_, parameters::vertex_point_map(vpmap_)
|
||||||
typename boost::property_traits<VertexPointMap>::reference
|
.geom_traits(gt_));
|
||||||
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_));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FaceRange>
|
template<typename FaceRange>
|
||||||
|
|
@ -1524,7 +1502,7 @@ private:
|
||||||
|
|
||||||
// update status using constrained edge map
|
// update status using constrained edge map
|
||||||
if (!boost::is_same<EdgeIsConstrainedMap,
|
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_))
|
BOOST_FOREACH(edge_descriptor e, edges(mesh_))
|
||||||
{
|
{
|
||||||
|
|
@ -1598,27 +1576,31 @@ private:
|
||||||
const bool collapse_constraints)
|
const bool collapse_constraints)
|
||||||
{
|
{
|
||||||
CGAL_assertion_code(std::size_t nb_done = 0);
|
CGAL_assertion_code(std::size_t nb_done = 0);
|
||||||
|
|
||||||
boost::unordered_set<halfedge_descriptor> degenerate_faces;
|
boost::unordered_set<halfedge_descriptor> degenerate_faces;
|
||||||
BOOST_FOREACH(halfedge_descriptor h,
|
BOOST_FOREACH(halfedge_descriptor h,
|
||||||
halfedges_around_target(halfedge(v, mesh_), mesh_))
|
halfedges_around_target(halfedge(v, mesh_), mesh_))
|
||||||
{
|
{
|
||||||
if (is_border(h, mesh_))
|
if(!is_border(h, mesh_) &&
|
||||||
continue;
|
is_degenerate_triangle_face(face(h, mesh_), mesh_,
|
||||||
if (is_degenerate_triangle_face(h, mesh_, vpmap_, GeomTraits()))
|
parameters::vertex_point_map(vpmap_)
|
||||||
|
.geom_traits(gt_)))
|
||||||
degenerate_faces.insert(h);
|
degenerate_faces.insert(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
while(!degenerate_faces.empty())
|
while(!degenerate_faces.empty())
|
||||||
{
|
{
|
||||||
halfedge_descriptor h = *(degenerate_faces.begin());
|
halfedge_descriptor h = *(degenerate_faces.begin());
|
||||||
degenerate_faces.erase(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
|
//this can happen when flipping h has consequences further in the mesh
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
//check that opposite is not also degenerate
|
//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_))
|
if(is_border(h, mesh_))
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1664,11 +1646,15 @@ private:
|
||||||
short_edges.insert(typename Bimap::value_type(hf, sqlen));
|
short_edges.insert(typename Bimap::value_type(hf, sqlen));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_border(hf, mesh_)
|
if(!is_border(hf, mesh_) &&
|
||||||
&& is_degenerate_triangle_face(hf, mesh_, vpmap_, GeomTraits()))
|
is_degenerate_triangle_face(face(hf, mesh_), mesh_,
|
||||||
|
parameters::vertex_point_map(vpmap_)
|
||||||
|
.geom_traits(gt_)))
|
||||||
degenerate_faces.insert(hf);
|
degenerate_faces.insert(hf);
|
||||||
if (!is_border(hfo, mesh_)
|
if(!is_border(hfo, mesh_) &&
|
||||||
&& is_degenerate_triangle_face(hfo, mesh_, vpmap_, GeomTraits()))
|
is_degenerate_triangle_face(face(hfo, mesh_), mesh_,
|
||||||
|
parameters::vertex_point_map(vpmap_)
|
||||||
|
.geom_traits(gt_)))
|
||||||
degenerate_faces.insert(hfo);
|
degenerate_faces.insert(hfo);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
@ -1685,9 +1671,10 @@ private:
|
||||||
BOOST_FOREACH(halfedge_descriptor h,
|
BOOST_FOREACH(halfedge_descriptor h,
|
||||||
halfedges_around_target(he, mesh_))
|
halfedges_around_target(he, mesh_))
|
||||||
{
|
{
|
||||||
if (is_border(h, mesh_))
|
if(!is_border(h, mesh_) &&
|
||||||
continue;
|
is_degenerate_triangle_face(face(h, mesh_), mesh_,
|
||||||
if (is_degenerate_triangle_face(h, mesh_, vpmap_, GeomTraits()))
|
parameters::vertex_point_map(vpmap_)
|
||||||
|
.geom_traits(gt_)))
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1828,10 +1815,10 @@ private:
|
||||||
{
|
{
|
||||||
std::cout << "Test self intersections...";
|
std::cout << "Test self intersections...";
|
||||||
std::vector<std::pair<face_descriptor, face_descriptor> > facets;
|
std::vector<std::pair<face_descriptor, face_descriptor> > facets;
|
||||||
PMP::self_intersections(
|
PMP::self_intersections(mesh_,
|
||||||
mesh_,
|
std::back_inserter(facets),
|
||||||
std::back_inserter(facets),
|
PMP::parameters::vertex_point_map(vpmap_)
|
||||||
PMP::parameters::vertex_point_map(vpmap_));
|
.geom_traits(gt_));
|
||||||
//CGAL_assertion(facets.empty());
|
//CGAL_assertion(facets.empty());
|
||||||
std::cout << "done ("<< facets.size() <<" facets)." << std::endl;
|
std::cout << "done ("<< facets.size() <<" facets)." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
@ -1840,11 +1827,11 @@ private:
|
||||||
{
|
{
|
||||||
std::cout << "Test self intersections...";
|
std::cout << "Test self intersections...";
|
||||||
std::vector<std::pair<face_descriptor, face_descriptor> > facets;
|
std::vector<std::pair<face_descriptor, face_descriptor> > facets;
|
||||||
PMP::self_intersections(
|
PMP::self_intersections(faces_around_target(halfedge(v, mesh_), mesh_),
|
||||||
faces_around_target(halfedge(v, mesh_), mesh_),
|
mesh_,
|
||||||
mesh_,
|
std::back_inserter(facets),
|
||||||
std::back_inserter(facets),
|
PMP::parameters::vertex_point_map(vpmap_)
|
||||||
PMP::parameters::vertex_point_map(vpmap_));
|
.geom_traits(gt_));
|
||||||
//CGAL_assertion(facets.empty());
|
//CGAL_assertion(facets.empty());
|
||||||
std::cout << "done ("<< facets.size() <<" facets)." << std::endl;
|
std::cout << "done ("<< facets.size() <<" facets)." << std::endl;
|
||||||
}
|
}
|
||||||
|
|
@ -1926,6 +1913,7 @@ private:
|
||||||
private:
|
private:
|
||||||
PolygonMesh& mesh_;
|
PolygonMesh& mesh_;
|
||||||
VertexPointMap& vpmap_;
|
VertexPointMap& vpmap_;
|
||||||
|
const GeomTraits& gt_;
|
||||||
bool build_tree_;
|
bool build_tree_;
|
||||||
bool has_border_;
|
bool has_border_;
|
||||||
std::vector<AABB_tree*> trees;
|
std::vector<AABB_tree*> trees;
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -174,10 +174,10 @@ void random_perturbation(VertexRange vertices
|
||||||
typedef typename boost::lookup_named_param_def <
|
typedef typename boost::lookup_named_param_def <
|
||||||
internal_np::vertex_is_constrained_t,
|
internal_np::vertex_is_constrained_t,
|
||||||
NamedParameters,
|
NamedParameters,
|
||||||
internal::No_constraint_pmap<vertex_descriptor>//default
|
Constant_property_map<vertex_descriptor, bool> // default
|
||||||
> ::type VCMap;
|
> ::type VCMap;
|
||||||
VCMap vcmap = choose_param(get_param(np, internal_np::vertex_is_constrained),
|
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);
|
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);
|
bool do_project = choose_param(get_param(np, internal_np::do_project), true);
|
||||||
|
|
|
||||||
|
|
@ -163,6 +163,7 @@ void isotropic_remeshing(const FaceRange& faces
|
||||||
boost::is_default_param(get_param(np, internal_np::projection_functor));
|
boost::is_default_param(get_param(np, internal_np::projection_functor));
|
||||||
|
|
||||||
typedef typename GetGeomTraits<PM, NamedParameters>::type GT;
|
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;
|
typedef typename GetVertexPointMap<PM, NamedParameters>::type VPMap;
|
||||||
VPMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
|
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 <
|
typedef typename boost::lookup_named_param_def <
|
||||||
internal_np::edge_is_constrained_t,
|
internal_np::edge_is_constrained_t,
|
||||||
NamedParameters,
|
NamedParameters,
|
||||||
internal::No_constraint_pmap<edge_descriptor>//default
|
Constant_property_map<edge_descriptor, bool> // default (no constraint pmap)
|
||||||
> ::type ECMap;
|
> ::type ECMap;
|
||||||
ECMap ecmap = choose_param(get_param(np, internal_np::edge_is_constrained)
|
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));
|
||||||
|
|
||||||
typedef typename boost::lookup_named_param_def <
|
typedef typename boost::lookup_named_param_def <
|
||||||
internal_np::vertex_is_constrained_t,
|
internal_np::vertex_is_constrained_t,
|
||||||
NamedParameters,
|
NamedParameters,
|
||||||
internal::No_constraint_pmap<vertex_descriptor>//default
|
Constant_property_map<vertex_descriptor, bool> // default (no constraint pmap)
|
||||||
> ::type VCMap;
|
> ::type VCMap;
|
||||||
VCMap vcmap = choose_param(get_param(np, internal_np::vertex_is_constrained),
|
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);
|
bool protect = choose_param(get_param(np, internal_np::protect_constraints), false);
|
||||||
typedef typename boost::lookup_named_param_def <
|
typedef typename boost::lookup_named_param_def <
|
||||||
|
|
@ -227,7 +228,7 @@ void isotropic_remeshing(const FaceRange& faces
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
typename internal::Incremental_remesher<PM, VPMap, GT, ECMap, VCMap, FPMap, FIMap>
|
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);
|
remesher.init_remeshing(faces);
|
||||||
|
|
||||||
#ifdef CGAL_PMP_REMESHING_VERBOSE
|
#ifdef CGAL_PMP_REMESHING_VERBOSE
|
||||||
|
|
@ -340,6 +341,8 @@ void split_long_edges(const EdgeRange& edges
|
||||||
using boost::get_param;
|
using boost::get_param;
|
||||||
|
|
||||||
typedef typename GetGeomTraits<PM, NamedParameters>::type GT;
|
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;
|
typedef typename GetVertexPointMap<PM, NamedParameters>::type VPMap;
|
||||||
VPMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
|
VPMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
|
||||||
get_property_map(vertex_point, pmesh));
|
get_property_map(vertex_point, pmesh));
|
||||||
|
|
@ -351,22 +354,21 @@ void split_long_edges(const EdgeRange& edges
|
||||||
typedef typename boost::lookup_named_param_def <
|
typedef typename boost::lookup_named_param_def <
|
||||||
internal_np::edge_is_constrained_t,
|
internal_np::edge_is_constrained_t,
|
||||||
NamedParameters,
|
NamedParameters,
|
||||||
internal::No_constraint_pmap<edge_descriptor>//default
|
Constant_property_map<edge_descriptor, bool> // default (no constraint pmap)
|
||||||
> ::type ECMap;
|
> ::type ECMap;
|
||||||
ECMap ecmap = choose_param(get_param(np, internal_np::edge_is_constrained),
|
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,
|
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>,
|
internal::Connected_components_pmap<PM, FIMap>,
|
||||||
FIMap
|
FIMap
|
||||||
>
|
>
|
||||||
remesher(pmesh, vpmap, false/*protect constraints*/
|
remesher(pmesh, vpmap, gt, false/*protect constraints*/, ecmap,
|
||||||
, ecmap
|
Constant_property_map<vertex_descriptor, bool>(false),
|
||||||
, internal::No_constraint_pmap<vertex_descriptor>()
|
internal::Connected_components_pmap<PM, FIMap>(faces(pmesh), pmesh, ecmap, fimap, false),
|
||||||
, internal::Connected_components_pmap<PM, FIMap>(faces(pmesh), pmesh, ecmap, fimap, false)
|
fimap,
|
||||||
, fimap
|
false/*need aabb_tree*/);
|
||||||
, false/*need aabb_tree*/);
|
|
||||||
|
|
||||||
remesher.split_long_edges(edges, max_length);
|
remesher.split_long_edges(edges, max_length);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,9 @@
|
||||||
|
|
||||||
#include <CGAL/license/Polygon_mesh_processing/repair.h>
|
#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/boost/graph/Euler_operations.h>
|
||||||
#include <CGAL/Union_find.h>
|
#include <CGAL/Union_find.h>
|
||||||
|
#include <CGAL/property_map.h>
|
||||||
#include <CGAL/algorithm.h>
|
#include <CGAL/algorithm.h>
|
||||||
#include <CGAL/array.h>
|
#include <CGAL/array.h>
|
||||||
|
|
||||||
|
|
@ -40,21 +37,33 @@
|
||||||
|
|
||||||
#include <CGAL/Polygon_mesh_processing/connected_components.h>
|
#include <CGAL/Polygon_mesh_processing/connected_components.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/border.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_function_params.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/internal/named_params_helper.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
|
#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
|
||||||
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
|
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
|
||||||
#include <CGAL/IO/OFF_reader.h>
|
#include <CGAL/IO/OFF_reader.h>
|
||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#endif
|
#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 CGAL{
|
||||||
namespace Polygon_mesh_processing {
|
namespace Polygon_mesh_processing {
|
||||||
|
|
||||||
namespace debug{
|
namespace debug{
|
||||||
|
|
||||||
template <class TriangleMesh, class VertexPointMap>
|
template <class TriangleMesh, class VertexPointMap>
|
||||||
std::ostream& dump_edge_neighborhood(
|
std::ostream& dump_edge_neighborhood(
|
||||||
typename boost::graph_traits<TriangleMesh>::edge_descriptor ed,
|
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";
|
<< vids[ target(next(next(halfedge(f, tm), tm), tm), tm) ] << "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} //end of namespace debug
|
} //end of namespace debug
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
template <class HalfedgeGraph, class VertexPointMap, class Traits>
|
template <class HalfedgeGraph, class VertexPointMap, class Traits>
|
||||||
struct Less_vertex_point{
|
struct Less_vertex_point{
|
||||||
typedef typename boost::graph_traits<HalfedgeGraph>::vertex_descriptor vertex_descriptor;
|
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>
|
/// \ingroup PMP_repairing_grp
|
||||||
OutputIterator
|
/// collects the degenerate edges within a given range of edges.
|
||||||
degenerate_faces(const TriangleMesh& tm,
|
///
|
||||||
const VertexPointMap& vpmap,
|
/// @tparam EdgeRange a model of `Range` with value type `boost::graph_traits<TriangleMesh>::%edge_descriptor`
|
||||||
const Traits& traits,
|
/// @tparam TriangleMesh a model of `HalfedgeGraph`
|
||||||
OutputIterator out)
|
/// @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;
|
typedef typename boost::graph_traits<TriangleMesh>::edge_descriptor edge_descriptor;
|
||||||
BOOST_FOREACH(face_descriptor fd, faces(tm))
|
|
||||||
|
BOOST_FOREACH(edge_descriptor ed, edges)
|
||||||
{
|
{
|
||||||
if ( is_degenerate_triangle_face(fd, tm, vpmap, traits) )
|
if(is_degenerate_edge(ed, tm, np))
|
||||||
*out++=fd;
|
*out++ = ed;
|
||||||
}
|
}
|
||||||
return out;
|
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>
|
template <class TriangleMesh, class OutputIterator>
|
||||||
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;
|
return degenerate_edges(edges(tm), tm, out, CGAL::parameters::all_default());
|
||||||
typedef typename boost::property_traits<Vpm>::value_type Point;
|
}
|
||||||
typedef typename Kernel_traits<Point>::Kernel Kernel;
|
|
||||||
|
|
||||||
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.
|
// 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>
|
template <class EdgeRange, class TriangleMesh, class NamedParameters>
|
||||||
std::size_t remove_null_edges(
|
bool remove_degenerate_edges(const EdgeRange& edge_range,
|
||||||
const EdgeRange& edge_range,
|
TriangleMesh& tmesh,
|
||||||
TriangleMesh& tmesh,
|
const NamedParameters& np)
|
||||||
const NamedParameters& np)
|
|
||||||
{
|
{
|
||||||
CGAL_assertion(CGAL::is_triangle_mesh(tmesh));
|
CGAL_assertion(CGAL::is_triangle_mesh(tmesh));
|
||||||
|
|
||||||
|
|
@ -385,27 +521,25 @@ std::size_t remove_null_edges(
|
||||||
typedef typename GetVertexPointMap<TM, NamedParameters>::type VertexPointMap;
|
typedef typename GetVertexPointMap<TM, NamedParameters>::type VertexPointMap;
|
||||||
VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
|
VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
|
||||||
get_property_map(vertex_point, tmesh));
|
get_property_map(vertex_point, tmesh));
|
||||||
|
|
||||||
typedef typename GetGeomTraits<TM, NamedParameters>::type Traits;
|
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;
|
std::size_t nb_deg_faces = 0;
|
||||||
|
bool all_removed=true;
|
||||||
|
|
||||||
// collect edges of length 0
|
// collect edges of length 0
|
||||||
std::set<edge_descriptor> null_edges_to_remove;
|
std::set<edge_descriptor> degenerate_edges_to_remove;
|
||||||
BOOST_FOREACH(edge_descriptor ed, edge_range)
|
degenerate_edges(edge_range, tmesh, std::inserter(degenerate_edges_to_remove,
|
||||||
{
|
degenerate_edges_to_remove.end()));
|
||||||
if ( traits.equal_3_object()(get(vpmap, target(ed, tmesh)), get(vpmap, source(ed, tmesh))) )
|
|
||||||
null_edges_to_remove.insert(ed);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
|
#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
|
||||||
std::cout << "Found " << null_edges_to_remove.size() << " null edges.\n";
|
std::cout << "Found " << degenerate_edges_to_remove.size() << " null edges.\n";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
while (!null_edges_to_remove.empty())
|
while (!degenerate_edges_to_remove.empty())
|
||||||
{
|
{
|
||||||
edge_descriptor ed = *null_edges_to_remove.begin();
|
edge_descriptor ed = *degenerate_edges_to_remove.begin();
|
||||||
null_edges_to_remove.erase(null_edges_to_remove.begin());
|
degenerate_edges_to_remove.erase(degenerate_edges_to_remove.begin());
|
||||||
|
|
||||||
halfedge_descriptor h = halfedge(ed, tmesh);
|
halfedge_descriptor h = halfedge(ed, tmesh);
|
||||||
|
|
||||||
|
|
@ -415,12 +549,12 @@ std::size_t remove_null_edges(
|
||||||
if ( face(h, tmesh)!=GT::null_face() )
|
if ( face(h, tmesh)!=GT::null_face() )
|
||||||
{
|
{
|
||||||
++nb_deg_faces;
|
++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())
|
if (face(opposite(h, tmesh), tmesh)!=GT::null_face())
|
||||||
{
|
{
|
||||||
++nb_deg_faces;
|
++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
|
//now remove the edge
|
||||||
CGAL::Euler::collapse_edge(ed, tmesh);
|
CGAL::Euler::collapse_edge(ed, tmesh);
|
||||||
|
|
@ -435,10 +569,40 @@ std::size_t remove_null_edges(
|
||||||
if (is_triangle(hd, tmesh))
|
if (is_triangle(hd, tmesh))
|
||||||
{
|
{
|
||||||
Euler::fill_hole(hd, tmesh);
|
Euler::fill_hole(hd, tmesh);
|
||||||
null_edges_to_remove.insert(ed);
|
degenerate_edges_to_remove.insert(ed);
|
||||||
continue;
|
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
|
// 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
|
// 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
|
// remove edges
|
||||||
BOOST_FOREACH(edge_descriptor ed, edges_to_remove)
|
BOOST_FOREACH(edge_descriptor ed, edges_to_remove)
|
||||||
{
|
{
|
||||||
null_edges_to_remove.erase(ed);
|
degenerate_edges_to_remove.erase(ed);
|
||||||
remove_edge(ed, tmesh);
|
remove_edge(ed, tmesh);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -641,61 +805,60 @@ std::size_t remove_null_edges(
|
||||||
put(vpmap, target(new_hd, tmesh), pt);
|
put(vpmap, target(new_hd, tmesh), pt);
|
||||||
|
|
||||||
BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_target(new_hd, tmesh))
|
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))) )
|
if(is_degenerate_edge(edge(hd, tmesh), tmesh, np))
|
||||||
null_edges_to_remove.insert(edge(hd, tmesh));
|
degenerate_edges_to_remove.insert(edge(hd, tmesh));
|
||||||
|
|
||||||
CGAL_assertion( is_valid_polygon_mesh(tmesh) );
|
CGAL_assertion( is_valid_polygon_mesh(tmesh) );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nb_deg_faces;
|
return all_removed;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class EdgeRange, class TriangleMesh>
|
template <class EdgeRange, class TriangleMesh>
|
||||||
std::size_t remove_null_edges(
|
bool remove_degenerate_edges(const EdgeRange& edge_range,
|
||||||
const EdgeRange& edge_range,
|
TriangleMesh& tmesh)
|
||||||
TriangleMesh& tmesh)
|
|
||||||
{
|
{
|
||||||
return remove_null_edges(edge_range, tmesh,
|
return remove_degenerate_edges(edge_range, tmesh, parameters::all_default());
|
||||||
parameters::all_default());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// \ingroup PMP_repairing_grp
|
// \ingroup PMP_repairing_grp
|
||||||
/// removes the degenerate faces from a triangulated surface mesh.
|
// removes the degenerate faces from a triangulated surface mesh.
|
||||||
/// A face is considered degenerate if two of its vertices share the same location,
|
// A face is considered degenerate if two of its vertices share the same location,
|
||||||
/// or more generally if all its vertices are collinear.
|
// or more generally if all its vertices are collinear.
|
||||||
///
|
//
|
||||||
/// @pre `CGAL::is_triangle_mesh(tmesh)`
|
// @pre `CGAL::is_triangle_mesh(tmesh)`
|
||||||
///
|
//
|
||||||
/// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph`
|
// @tparam TriangleMesh a model of `FaceListGraph` and `MutableFaceGraph`
|
||||||
/// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
|
// @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters"
|
||||||
///
|
//
|
||||||
/// @param tmesh the triangulated surface mesh to be repaired
|
// @param tmesh the triangulated surface mesh to be repaired
|
||||||
/// @param np optional \ref pmp_namedparameters "Named Parameters" described below
|
// @param np optional \ref pmp_namedparameters "Named Parameters" described below
|
||||||
///
|
//
|
||||||
/// \cgalNamedParamsBegin
|
// \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`.
|
// \cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `pmesh`.
|
||||||
/// If this parameter is omitted, an internal property map for
|
// The type of this map is model of `ReadWritePropertyMap`.
|
||||||
/// `CGAL::vertex_point_t` must be available in `TriangleMesh`
|
// If this parameter is omitted, an internal property map for
|
||||||
/// \cgalParamEnd
|
// `CGAL::vertex_point_t` must be available in `TriangleMesh`
|
||||||
/// \cgalParamBegin{geom_traits} a geometric traits class instance.
|
// \cgalParamEnd
|
||||||
/// The traits class must provide the nested type `Point_3`,
|
// \cgalParamBegin{geom_traits} a geometric traits class instance.
|
||||||
/// and the nested functors :
|
// The traits class must provide the nested type `Point_3`,
|
||||||
/// - `Compare_distance_3` to compute the distance between 2 points
|
// and the nested functors :
|
||||||
/// - `Collinear_are_ordered_along_line_3` to check whether 3 collinear points are ordered
|
// - `Compare_distance_3` to compute the distance between 2 points
|
||||||
/// - `Collinear_3` to check whether 3 points are collinear
|
// - `Collinear_3` to check whether 3 points are collinear
|
||||||
/// - `Less_xyz_3` to compare lexicographically two points
|
// - `Less_xyz_3` to compare lexicographically two points
|
||||||
/// - `Equal_3` to check whether 2 points are identical
|
// - `Equal_3` to check whether 2 points are identical
|
||||||
/// - for each functor Foo, a function `Foo foo_object()`
|
// For each functor `Foo`, a function `Foo foo_object()`
|
||||||
/// \cgalParamEnd
|
// \cgalParamEnd
|
||||||
/// \cgalNamedParamsEnd
|
// \cgalNamedParamsEnd
|
||||||
///
|
//
|
||||||
/// \todo the function might not be able to remove all degenerate faces.
|
// \todo the function might not be able to remove all degenerate faces.
|
||||||
/// We should probably do something with the return type.
|
// We should probably do something with the return type.
|
||||||
/// \return number of removed degenerate faces
|
//
|
||||||
|
/// \return `true` if all degenerate faces were successfully removed, and `false` otherwise.
|
||||||
template <class TriangleMesh, class NamedParameters>
|
template <class TriangleMesh, class NamedParameters>
|
||||||
std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
|
bool remove_degenerate_faces( TriangleMesh& tmesh,
|
||||||
const NamedParameters& np)
|
const NamedParameters& np)
|
||||||
{
|
{
|
||||||
CGAL_assertion(CGAL::is_triangle_mesh(tmesh));
|
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>::value_type Point_3;
|
||||||
typedef typename boost::property_traits<VertexPointMap>::reference Point_ref;
|
typedef typename boost::property_traits<VertexPointMap>::reference Point_ref;
|
||||||
|
|
||||||
// First remove edges of length 0
|
// 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
|
#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
|
// Then, remove triangles made of 3 collinear points
|
||||||
std::set<face_descriptor> degenerate_face_set;
|
std::set<face_descriptor> degenerate_face_set;
|
||||||
BOOST_FOREACH(face_descriptor fd, faces(tmesh))
|
degenerate_faces(tmesh, std::inserter(degenerate_face_set, degenerate_face_set.begin()), np);
|
||||||
if ( is_degenerate_triangle_face(fd, tmesh, vpmap, traits) )
|
// Ignore faces with null edges
|
||||||
degenerate_face_set.insert(fd);
|
if (!all_removed)
|
||||||
nb_deg_faces+=degenerate_face_set.size();
|
{
|
||||||
|
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
|
// first remove degree 3 vertices that are part of a cap
|
||||||
// (only the vertex in the middle of the opposite edge)
|
// (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))
|
BOOST_FOREACH(halfedge_descriptor hd, halfedges_around_face(halfedge(fd, tmesh), tmesh))
|
||||||
{
|
{
|
||||||
vertex_descriptor vd = target(hd, 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);
|
vertices_to_remove.insert(vd);
|
||||||
break;
|
break;
|
||||||
|
|
@ -763,7 +938,7 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
|
||||||
degenerate_face_set.erase( face(hd2, tmesh) );
|
degenerate_face_set.erase( face(hd2, tmesh) );
|
||||||
// remove the central vertex and check if the new face is degenerated
|
// remove the central vertex and check if the new face is degenerated
|
||||||
hd=CGAL::Euler::remove_center_vertex(hd, tmesh);
|
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) );
|
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);
|
Euler::flip_edge(edge_to_flip, tmesh);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
all_removed=false;
|
||||||
#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
|
#ifdef CGAL_PMP_REMOVE_DEGENERATE_FACES_DEBUG
|
||||||
else{
|
|
||||||
std::cout << " WARNING: flip is not possible\n";
|
std::cout << " WARNING: flip is not possible\n";
|
||||||
// \todo Let p and q be the vertices opposite to `edge_to_flip`, and let
|
// \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
|
// 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
|
// the edge `pq`. In that case I think we should remove all the triangles
|
||||||
// so that the triangle pqr is in the mesh.
|
// so that the triangle pqr is in the mesh.
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -964,7 +1141,7 @@ std::size_t remove_degenerate_faces(TriangleMesh& tmesh,
|
||||||
// preliminary step to check if the operation is possible
|
// preliminary step to check if the operation is possible
|
||||||
// sort the boundary points along the common supporting line
|
// sort the boundary points along the common supporting line
|
||||||
// we first need a reference point
|
// 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<
|
std::pair<
|
||||||
typename std::set<vertex_descriptor>::iterator,
|
typename std::set<vertex_descriptor>::iterator,
|
||||||
typename std::set<vertex_descriptor>::iterator > ref_vertices =
|
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>
|
template<class TriangleMesh>
|
||||||
std::size_t remove_degenerate_faces(TriangleMesh& tmesh)
|
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());
|
CGAL::Polygon_mesh_processing::parameters::all_default());
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class TriangleMesh, class Vpm>
|
namespace internal {
|
||||||
std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, Vpm vpm)
|
|
||||||
|
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 boost::graph_traits<TriangleMesh> GT;
|
||||||
typedef typename GT::vertex_descriptor vertex_descriptor;
|
typedef typename GT::vertex_descriptor vertex_descriptor;
|
||||||
typedef typename GT::halfedge_descriptor halfedge_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<vertex_descriptor> vertices_handled;
|
||||||
boost::unordered_set<halfedge_descriptor> halfedges_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;
|
std::vector<halfedge_descriptor> non_manifold_cones;
|
||||||
BOOST_FOREACH(halfedge_descriptor h, halfedges(tm))
|
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);
|
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);
|
non_manifold_cones.push_back(h);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
set_halfedge(vd, h, tm);
|
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);
|
halfedges_handled.insert(h);
|
||||||
h=opposite(next(h, tm), tm);
|
h = opposite(next(h, tm), tm);
|
||||||
}while(h!=start);
|
}
|
||||||
|
while(h != start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!non_manifold_cones.empty()) {
|
if(!non_manifold_cones.empty())
|
||||||
|
{
|
||||||
BOOST_FOREACH(halfedge_descriptor h, non_manifold_cones)
|
BOOST_FOREACH(halfedge_descriptor h, non_manifold_cones)
|
||||||
{
|
{
|
||||||
halfedge_descriptor start = h;
|
halfedge_descriptor start = h;
|
||||||
vertex_descriptor new_vd = add_vertex(tm);
|
vertex_descriptor new_vd = add_vertex(tm);
|
||||||
++nb_new_vertices;
|
++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)));
|
put(vpm, new_vd, get(vpm, target(h, tm)));
|
||||||
set_halfedge(new_vd, h, tm);
|
set_halfedge(new_vd, h, tm);
|
||||||
do{
|
do
|
||||||
|
{
|
||||||
set_target(h, new_vd, tm);
|
set_target(h, new_vd, tm);
|
||||||
h=opposite(next(h, tm), tm);
|
h = opposite(next(h, tm), tm);
|
||||||
} while(h!=start);
|
}
|
||||||
|
while(h != start);
|
||||||
}
|
}
|
||||||
|
dmap.dump(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
return nb_new_vertices;
|
return nb_new_vertices;
|
||||||
|
|
@ -1343,12 +1674,9 @@ std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm, Vpm vpm)
|
||||||
template <class TriangleMesh>
|
template <class TriangleMesh>
|
||||||
std::size_t duplicate_non_manifold_vertices(TriangleMesh& tm)
|
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
|
/// \ingroup PMP_repairing_grp
|
||||||
/// removes the isolated vertices from any polygon mesh.
|
/// removes the isolated vertices from any polygon mesh.
|
||||||
/// A vertex is considered isolated if it is not incident to any simplex
|
/// A vertex is considered isolated if it is not incident to any simplex
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -49,6 +49,8 @@
|
||||||
#include <CGAL/Polygon_mesh_processing/distance.h>
|
#include <CGAL/Polygon_mesh_processing/distance.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/intersection.h>
|
#include <CGAL/Polygon_mesh_processing/intersection.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/transform.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
|
// the named parameter header being not documented the doc is put here for now
|
||||||
#ifdef DOXYGEN_RUNNING
|
#ifdef DOXYGEN_RUNNING
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,9 @@ endif()
|
||||||
create_single_source_cgal_program("test_orient_cc.cpp")
|
create_single_source_cgal_program("test_orient_cc.cpp")
|
||||||
create_single_source_cgal_program("test_pmp_transform.cpp")
|
create_single_source_cgal_program("test_pmp_transform.cpp")
|
||||||
create_single_source_cgal_program("extrude_test.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 )
|
if( TBB_FOUND )
|
||||||
CGAL_target_use_TBB(test_pmp_distance)
|
CGAL_target_use_TBB(test_pmp_distance)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
OFF
|
||||||
|
3 1 0
|
||||||
|
0 0 0
|
||||||
|
1 0 0
|
||||||
|
0 0 0
|
||||||
|
3 0 1 2
|
||||||
|
|
@ -11,11 +11,34 @@
|
||||||
//the last test (on trihole.off) does not terminate
|
//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);
|
std::ifstream input(fname);
|
||||||
|
|
||||||
|
|
@ -24,20 +47,22 @@ void fix(const char* fname)
|
||||||
std::cerr << fname << " is not a valid off file.\n";
|
std::cerr << fname << " is not a valid off file.\n";
|
||||||
exit(1);
|
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) );
|
assert( CGAL::is_valid_polygon_mesh(mesh) );
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
fix("data_degeneracies/degtri_2dt_1edge_split_twice.off");
|
fix_degeneracies("data_degeneracies/degtri_2dt_1edge_split_twice.off");
|
||||||
fix("data_degeneracies/degtri_four-2.off");
|
fix_degeneracies("data_degeneracies/degtri_four-2.off");
|
||||||
fix("data_degeneracies/degtri_four.off");
|
fix_degeneracies("data_degeneracies/degtri_four.off");
|
||||||
fix("data_degeneracies/degtri_on_border.off");
|
fix_degeneracies("data_degeneracies/degtri_on_border.off");
|
||||||
fix("data_degeneracies/degtri_three.off");
|
fix_degeneracies("data_degeneracies/degtri_three.off");
|
||||||
fix("data_degeneracies/degtri_single.off");
|
fix_degeneracies("data_degeneracies/degtri_single.off");
|
||||||
fix("data_degeneracies/trihole.off");
|
fix_degeneracies("data_degeneracies/trihole.off");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,9 @@
|
||||||
#include <CGAL/box_intersection_d.h>
|
#include <CGAL/box_intersection_d.h>
|
||||||
#include <CGAL/Make_triangle_soup.h>
|
#include <CGAL/Make_triangle_soup.h>
|
||||||
#include <CGAL/Kernel_traits.h>
|
#include <CGAL/Kernel_traits.h>
|
||||||
|
|
||||||
|
#include <CGAL/Polygon_mesh_processing/shape_predicates.h>
|
||||||
|
|
||||||
#ifdef USE_SURFACE_MESH
|
#ifdef USE_SURFACE_MESH
|
||||||
typedef Scene_surface_mesh_item Scene_facegraph_item;
|
typedef Scene_surface_mesh_item Scene_facegraph_item;
|
||||||
#else
|
#else
|
||||||
|
|
@ -80,14 +83,12 @@ template<class Mesh>
|
||||||
bool isDegen(Mesh* mesh, std::vector<typename boost::graph_traits<Mesh>::face_descriptor> &out_faces)
|
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::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
|
//filter non-triangle_faces
|
||||||
BOOST_FOREACH(FaceDescriptor f, faces(*mesh))
|
BOOST_FOREACH(FaceDescriptor f, faces(*mesh))
|
||||||
{
|
{
|
||||||
if(is_triangle(halfedge(f, *mesh), *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);
|
out_faces.push_back(f);
|
||||||
}
|
}
|
||||||
return !out_faces.empty();
|
return !out_faces.empty();
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ public:
|
||||||
actionStitchCloseBorderHalfedges->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental");
|
actionStitchCloseBorderHalfedges->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental");
|
||||||
actionRemoveSelfIntersections->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental");
|
actionRemoveSelfIntersections->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental");
|
||||||
actionRemoveIsolatedVertices->setProperty("subMenuName", "Polygon Mesh Processing/Repair");
|
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");
|
actionAutorefine->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental");
|
||||||
actionAutorefineAndRMSelfIntersections->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental");
|
actionAutorefineAndRMSelfIntersections->setProperty("subMenuName", "Polygon Mesh Processing/Repair/Experimental");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@
|
||||||
#include <CGAL/boost/graph/split_graph_into_polylines.h>
|
#include <CGAL/boost/graph/split_graph_into_polylines.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/border.h>
|
#include <CGAL/Polygon_mesh_processing/border.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/repair.h>
|
#include <CGAL/Polygon_mesh_processing/repair.h>
|
||||||
|
#include <CGAL/Polygon_mesh_processing/shape_predicates.h>
|
||||||
#include <Scene.h>
|
#include <Scene.h>
|
||||||
|
|
||||||
#ifdef USE_SURFACE_MESH
|
#ifdef USE_SURFACE_MESH
|
||||||
|
|
@ -741,14 +742,10 @@ public Q_SLOTS:
|
||||||
//Edition mode
|
//Edition mode
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
VPmap vpmap = get(CGAL::vertex_point, *selection_item->polyhedron());
|
|
||||||
bool is_valid = true;
|
bool is_valid = true;
|
||||||
BOOST_FOREACH(boost::graph_traits<Face_graph>::face_descriptor fd, faces(*selection_item->polyhedron()))
|
BOOST_FOREACH(boost::graph_traits<Face_graph>::face_descriptor fd, faces(*selection_item->polyhedron()))
|
||||||
{
|
{
|
||||||
if (CGAL::is_degenerate_triangle_face(fd,
|
if (CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(fd, *selection_item->polyhedron()))
|
||||||
*selection_item->polyhedron(),
|
|
||||||
vpmap,
|
|
||||||
CGAL::Kernel_traits< boost::property_traits<VPmap>::value_type >::Kernel()))
|
|
||||||
{
|
{
|
||||||
is_valid = false;
|
is_valid = false;
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include "Scene_edit_polyhedron_item.h"
|
#include "Scene_edit_polyhedron_item.h"
|
||||||
#include "Scene_polyhedron_selection_item.h"
|
#include "Scene_polyhedron_selection_item.h"
|
||||||
#include <CGAL/Polygon_mesh_processing/repair.h>
|
#include <CGAL/Polygon_mesh_processing/repair.h>
|
||||||
|
#include <CGAL/Polygon_mesh_processing/shape_predicates.h>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
|
@ -421,10 +422,7 @@ void Polyhedron_demo_edit_polyhedron_plugin::dock_widget_visibility_changed(bool
|
||||||
bool is_valid = true;
|
bool is_valid = true;
|
||||||
BOOST_FOREACH(boost::graph_traits<Face_graph>::face_descriptor fd, faces(*poly_item->face_graph()))
|
BOOST_FOREACH(boost::graph_traits<Face_graph>::face_descriptor fd, faces(*poly_item->face_graph()))
|
||||||
{
|
{
|
||||||
if (CGAL::is_degenerate_triangle_face(fd,
|
if (CGAL::Polygon_mesh_processing::is_degenerate_triangle_face(fd, *poly_item->face_graph()))
|
||||||
*poly_item->face_graph(),
|
|
||||||
get(boost::vertex_point,
|
|
||||||
*poly_item->face_graph()), Kernel()))
|
|
||||||
{
|
{
|
||||||
is_valid = false;
|
is_valid = false;
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
#include <CGAL/Polygon_mesh_processing/connected_components.h>
|
#include <CGAL/Polygon_mesh_processing/connected_components.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/measure.h>
|
#include <CGAL/Polygon_mesh_processing/measure.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/self_intersections.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/polygon_soup_to_polygon_mesh.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/orient_polygon_soup.h>
|
#include <CGAL/Polygon_mesh_processing/orient_polygon_soup.h>
|
||||||
#include <CGAL/boost/graph/selection.h>
|
#include <CGAL/boost/graph/selection.h>
|
||||||
|
|
@ -287,7 +288,7 @@ void* Scene_polyhedron_item_priv::get_aabb_tree()
|
||||||
int index =0;
|
int index =0;
|
||||||
BOOST_FOREACH( Polyhedron::Facet_iterator f, faces(*poly))
|
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;
|
continue;
|
||||||
if(!f->is_triangle())
|
if(!f->is_triangle())
|
||||||
{
|
{
|
||||||
|
|
@ -1677,7 +1678,7 @@ QString Scene_polyhedron_item::computeStats(int type)
|
||||||
if (d->poly->is_pure_triangle())
|
if (d->poly->is_pure_triangle())
|
||||||
{
|
{
|
||||||
if (d->number_of_degenerated_faces == (unsigned int)(-1))
|
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);
|
return QString::number(d->number_of_degenerated_faces);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#include "Scene_polyhedron_selection_item.h"
|
#include "Scene_polyhedron_selection_item.h"
|
||||||
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
|
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/repair.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/dijkstra_shortest_paths.h>
|
||||||
#include <CGAL/boost/graph/helpers.h>
|
#include <CGAL/boost/graph/helpers.h>
|
||||||
#include <CGAL/property_map.h>
|
#include <CGAL/property_map.h>
|
||||||
|
|
@ -2076,8 +2077,9 @@ bool Scene_polyhedron_selection_item_priv::canAddFace(fg_halfedge_descriptor hc,
|
||||||
found = true;
|
found = true;
|
||||||
fg_halfedge_descriptor res =
|
fg_halfedge_descriptor res =
|
||||||
CGAL::Euler::add_face_to_border(t,hc, *item->polyhedron());
|
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());
|
CGAL::Euler::remove_face(res, *item->polyhedron());
|
||||||
tempInstructions("Edge not selected : resulting facet is degenerated.",
|
tempInstructions("Edge not selected : resulting facet is degenerated.",
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@
|
||||||
#include <CGAL/Polygon_mesh_processing/connected_components.h>
|
#include <CGAL/Polygon_mesh_processing/connected_components.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
|
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/self_intersections.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/orient_polygon_soup.h>
|
||||||
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
|
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
|
||||||
#include "triangulate_primitive.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))
|
BOOST_FOREACH( face_descriptor f, faces(*sm))
|
||||||
{
|
{
|
||||||
//if face is degenerate, skip it
|
//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;
|
continue;
|
||||||
//if face not triangle, triangulate corresponding primitive before adding it to the tree
|
//if face not triangle, triangulate corresponding primitive before adding it to the tree
|
||||||
if(!CGAL::is_triangle(halfedge(f, *sm), *sm))
|
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(is_triangle_mesh(*d->smesh_))
|
||||||
{
|
{
|
||||||
if (d->number_of_degenerated_faces == (unsigned int)(-1))
|
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);
|
return QString::number(d->number_of_degenerated_faces);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
#ifndef POLYHEDRON_DEMO_STATISTICS_HELPERS_H
|
#ifndef POLYHEDRON_DEMO_STATISTICS_HELPERS_H
|
||||||
#define 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/accumulators.hpp>
|
||||||
#include <boost/accumulators/statistics/stats.hpp>
|
#include <boost/accumulators/statistics/stats.hpp>
|
||||||
|
|
@ -9,10 +10,13 @@
|
||||||
#include <boost/accumulators/statistics/min.hpp>
|
#include <boost/accumulators/statistics/min.hpp>
|
||||||
#include <boost/accumulators/statistics/max.hpp>
|
#include <boost/accumulators/statistics/max.hpp>
|
||||||
#include <boost/accumulators/statistics/median.hpp>
|
#include <boost/accumulators/statistics/median.hpp>
|
||||||
#include <CGAL/squared_distance_3_0.h>
|
|
||||||
#include <map>
|
|
||||||
#include <boost/property_map/property_map.hpp>
|
#include <boost/property_map/property_map.hpp>
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <iterator>
|
||||||
|
#include <limits>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
template<typename Mesh>
|
template<typename Mesh>
|
||||||
void angles(Mesh* poly, double& mini, double& maxi, double& ave)
|
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);
|
mid = extract_result< tag::median >(acc);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Mesh, typename VPmap>
|
template<typename Mesh>
|
||||||
unsigned int nb_degenerate_faces(Mesh* poly, VPmap vpmap)
|
unsigned int nb_degenerate_faces(Mesh* poly)
|
||||||
{
|
{
|
||||||
typedef typename boost::graph_traits<Mesh>::face_descriptor face_descriptor;
|
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;
|
std::vector<face_descriptor> degenerate_faces;
|
||||||
BOOST_FOREACH(face_descriptor f, faces(*poly))
|
CGAL::Polygon_mesh_processing::degenerate_faces(*poly, std::back_inserter(degenerate_faces));
|
||||||
{
|
|
||||||
if (CGAL::is_degenerate_triangle_face(f, *poly, vpmap, Traits()))
|
return static_cast<unsigned int>(degenerate_faces.size());
|
||||||
++nb;
|
|
||||||
}
|
|
||||||
return nb;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Mesh>
|
template<typename Mesh>
|
||||||
|
|
|
||||||
|
|
@ -462,10 +462,11 @@ make_property_map(const std::vector<T>& v)
|
||||||
template<class KeyType, class ValueType>
|
template<class KeyType, class ValueType>
|
||||||
struct Constant_property_map
|
struct Constant_property_map
|
||||||
{
|
{
|
||||||
const ValueType default_value;
|
ValueType default_value;
|
||||||
|
|
||||||
typedef KeyType key_type;
|
typedef KeyType key_type;
|
||||||
typedef ValueType value_type;
|
typedef ValueType value_type;
|
||||||
|
typedef value_type& reference;
|
||||||
typedef boost::read_write_property_map_tag category;
|
typedef boost::read_write_property_map_tag category;
|
||||||
|
|
||||||
Constant_property_map(const value_type& default_value = value_type()) : default_value (default_value) { }
|
Constant_property_map(const value_type& default_value = value_type()) : default_value (default_value) { }
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue