diff --git a/BGL/include/CGAL/boost/graph/parameters_interface.h b/BGL/include/CGAL/boost/graph/parameters_interface.h
index bc2a3a54dee..6811c71ca92 100644
--- a/BGL/include/CGAL/boost/graph/parameters_interface.h
+++ b/BGL/include/CGAL/boost/graph/parameters_interface.h
@@ -83,6 +83,10 @@ CGAL_add_named_parameter(output_iterator_t, output_iterator, output_iterator)
CGAL_add_named_parameter(erase_all_duplicates_t, erase_all_duplicates, erase_all_duplicates)
CGAL_add_named_parameter(require_same_orientation_t, require_same_orientation, require_same_orientation)
CGAL_add_named_parameter(use_safety_constraints_t, use_safety_constraints, use_safety_constraints)
+CGAL_add_named_parameter(use_angle_smoothing_t, use_angle_smoothing, use_angle_smoothing)
+CGAL_add_named_parameter(use_area_smoothing_t, use_area_smoothing, use_area_smoothing)
+CGAL_add_named_parameter(use_Delaunay_flips_t, use_Delaunay_flips, use_Delaunay_flips)
+CGAL_add_named_parameter(do_project_t, do_project, do_project)
// 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)
@@ -91,7 +95,6 @@ CGAL_add_named_parameter(get_placement_policy_t, get_placement_policy, get_place
//to be documented
CGAL_add_named_parameter(face_normal_t, face_normal, face_normal_map)
CGAL_add_named_parameter(random_seed_t, random_seed, random_seed)
-CGAL_add_named_parameter(do_project_t, do_project, do_project)
CGAL_add_named_parameter(tolerance_map_t, tolerance_map, tolerance_map)
//internal
diff --git a/BGL/test/BGL/test_cgal_bgl_named_params.cpp b/BGL/test/BGL/test_cgal_bgl_named_params.cpp
index 84e9cae9a2b..b3f54fb61fa 100644
--- a/BGL/test/BGL/test_cgal_bgl_named_params.cpp
+++ b/BGL/test/BGL/test_cgal_bgl_named_params.cpp
@@ -88,9 +88,12 @@ void test(const NamedParameters& np)
assert(get_param(np, CGAL::internal_np::erase_all_duplicates).v == 48);
assert(get_param(np, CGAL::internal_np::require_same_orientation).v == 49);
assert(get_param(np, CGAL::internal_np::use_bool_op_to_clip_surface).v == 50);
+ assert(get_param(np, CGAL::internal_np::use_angle_smoothing).v == 51);
+ assert(get_param(np, CGAL::internal_np::use_area_smoothing).v == 52);
+ assert(get_param(np, CGAL::internal_np::use_safety_constraints).v == 53);
+ assert(get_param(np, CGAL::internal_np::use_bool_op_to_clip_surface).v == 54);
assert(get_param(np, CGAL::internal_np::use_safety_constraints).v == 56);
-
// Named parameters that we use in the package 'Surface Mesh Simplification'
assert(get_param(np, CGAL::internal_np::get_cost_policy).v == 34);
assert(get_param(np, CGAL::internal_np::get_placement_policy).v == 35);
@@ -171,8 +174,12 @@ void test(const NamedParameters& np)
check_same_type<48>(get_param(np, CGAL::internal_np::erase_all_duplicates));
check_same_type<49>(get_param(np, CGAL::internal_np::require_same_orientation));
check_same_type<50>(get_param(np, CGAL::internal_np::use_bool_op_to_clip_surface));
+ check_same_type<51>(get_param(np, CGAL::internal_np::use_angle_smoothing));
+ check_same_type<52>(get_param(np, CGAL::internal_np::use_area_smoothing));
+ check_same_type<53>(get_param(np, CGAL::internal_np::use_Delaunay_flips));
+ check_same_type<54>(get_param(np, CGAL::internal_np::use_safety_constraints));
check_same_type<56>(get_param(np, CGAL::internal_np::use_safety_constraints));
-7
+
// Named parameters that we use in the package 'Surface Mesh Simplification'
check_same_type<34>(get_param(np, CGAL::internal_np::get_cost_policy));
check_same_type<35>(get_param(np, CGAL::internal_np::get_placement_policy));
@@ -246,16 +253,19 @@ int main()
.weight_calculator(A<39>(39))
.preserve_genus(A<40>(40))
.verbosity_level(A<41>(41))
- .use_binary_mode(A<51>(51))
.projection_functor(A<42>(42))
.throw_on_self_intersection(A<43>(43))
.clip_volume(A<44>(44))
.use_compact_clipper(A<45>(45))
- .use_bool_op_to_clip_surface(A<50>(50))
.apply_per_connected_component(A<46>(46))
.output_iterator(A<47>(47))
.erase_all_duplicates(A<48>(48))
- .require_same_orientation(A<52>(52))
+ .require_same_orientation(A<49>(49))
+ .use_bool_op_to_clip_surface(A<50>(50))
+ .use_binary_mode(A<51>(51))
+ .use_angle_smoothing_t(A<52>(52))
+ .use_area_smoothing_t(A<53>(53))
+ .use_Delaunay_flips_t(A<54>(54))
.use_safety_constraints(A<56>(56))
);
diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md
index 6af966d7056..b3c1138248b 100644
--- a/Installation/CHANGES.md
+++ b/Installation/CHANGES.md
@@ -27,8 +27,8 @@ Release date: September 2019
### Polygon Mesh Processing
- Added the function `CGAL::Polygon_mesh_processing::centroid()` which computes
the centroid of a closed triangle mesh.
-- Added the mesh smoothing functions `smooth_areas()` and `smooth_angles()`, which can be used to
- improve the quality of triangle elements based on geometric characteristics.
+- Added the mesh smoothing function `smooth()`, which can be used to
+ improve the quality of triangle elements based on various geometric characteristics.
- Added the shape smoothing function `smooth_along_curvature_flow()`, which can be used to
smooth the surface of a triangle mesh, using the mean curvature flow to perform noise removal.
diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt
index 2a66e57f543..45324ef144f 100644
--- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt
+++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/NamedParameters.txt
@@ -391,11 +391,52 @@ if orientation should matter when determining whether two faces are duplicates.
Default: `false`
\cgalNPEnd
+\cgalNPBegin{use_angle_smoothing_t} \anchor PMP_use_angle_smoothing
+Parameter used in the function `smooth()` to indicate if angle-based smoothing should be used.
+When this type of smoothing is used, the algorithm attempts to equalize angles incident to each vertex.
+
+\n
+Type: `bool` \n
+Default: `true`
+\cgalNPEnd
+
+\cgalNPBegin{use_area_smoothing_t} \anchor PMP_use_area_smoothing
+Parameter used in the function `smooth()` to indicate if area-based smoothing should be used.
+When this type of smoothing is used, the algorithm attempts to equalize the areas of the triangles
+incident to each vertex. Since this can create elongated triangles, a second phase uses Delaunay-based
+flips to recover good shapes, unless specified otherwise (see below).
+
+\n
+Type: `bool` \n
+Default: `true`
+\cgalNPEnd
+
+\cgalNPBegin{use_Delaunay_flips_t} \anchor PMP_use_Delaunay_flips
+Parameter used in the function `smooth()` to indicate if Delaunay-based flips should be used
+after area-based smoothing has been performed. A user wishing to preserve combinatorial information
+can set this parameter to `false`, but the mesh might have elongated elements.
+
+\n
+Type: `bool` \n
+Default: `true`
+\cgalNPEnd
+
\cgalNPBegin{use_safety_constraints} \anchor PMP_use_safety_constraints
-Parameter used in the functions `smooth_angles()` and `smooth_areas()` to indicate
-if some sanity checks should be used to decide if the move of a vertex should be applied or rejected.
-These sanity checks consists of checking that no face incident to the vertex becomes inverted
-and that the minimum angle of the incident faces is not decreased by the move.
+Parameter used in the function `smooth()` to indicate if some sanity checks should be used to decide
+if the move of a vertex should be applied or rejected. These sanity checks consists of checking that
+no face incident to the vertex becomes inverted and that the minimum angle of the incident faces
+is not decreased by the move.
+
+\n
+Type: `bool` \n
+Default: `true`
+\cgalNPEnd
+
+\cgalNPBegin{use_safety_constraints} \anchor PMP_use_safety_constraints
+Parameter used in the function `smooth()` to indicate if some sanity checks should be used to decide
+if the move of a vertex should be applied or rejected. These sanity checks consists of checking that
+no face incident to the vertex becomes inverted and that the minimum angle of the incident faces
+is not decreased by the move.
\n
Type: `bool` \n
diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt
index 4c84455d649..5651b269a4f 100644
--- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt
+++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt
@@ -96,8 +96,7 @@ and provides a list of the parameters that are used in this package.
\cgalCRPSection{Meshing Functions}
- `CGAL::Polygon_mesh_processing::fair()`
- `CGAL::Polygon_mesh_processing::refine()`
-- `CGAL::Polygon_mesh_processing::smooth_angles()`
-- `CGAL::Polygon_mesh_processing::smooth_areas()`
+- `CGAL::Polygon_mesh_processing::smooth()`
- `CGAL::Polygon_mesh_processing::smooth_along_curvature_flow()`
- `CGAL::Polygon_mesh_processing::triangulate_face()`
- `CGAL::Polygon_mesh_processing::triangulate_faces()`
diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt
index d81ac598fc9..e41add12a48 100644
--- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt
+++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/Polygon_mesh_processing.txt
@@ -130,8 +130,8 @@ While mesh smoothing is achieved by improving the quality of triangles based on
shape smoothing is designed to be \e intrinsic, depending as little as possible on the discretization
and smoothing the shape alone without optimizing the shape of the triangles.
-- Mesh smoothing: `CGAL::Polygon_mesh_processing::smooth_angles()` moves vertices so that angles between incident edges equalize,
-and `CGAL::Polygon_mesh_processing::smooth_areas()` moves vertices so that areas of adjacent triangles tend to equalize.
+- Mesh smoothing: `CGAL::Polygon_mesh_processing::smooth()` moves vertices to optimize geometry around each vertex:
+it can try to equalize the angles between incident edges, or (and) move vertices so that areas of adjacent triangles tend to equalize.
Border vertices are considered constrained and do not move at any step of the procedure. No vertices are inserted at any time.
Angle and area smoothing algorithms are based on Surazhsky and Gotsman \cgalCite{cgal:sg-hqct-04}.
Since area smoothing considers only areas as a smoothing criterion, it may result in long and skinny
diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt
index d41cc8f8b46..16368bfb493 100644
--- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt
+++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt
@@ -69,6 +69,8 @@ if (EIGEN3_FOUND)
create_single_source_cgal_program( "hole_filling_example_SM.cpp" )
create_single_source_cgal_program( "refine_fair_example.cpp")
create_single_source_cgal_program( "shape_smoothing_example.cpp")
+ create_single_source_cgal_program( "mesh_smoothing_example.cpp")
+
endif(EIGEN3_FOUND)
create_single_source_cgal_program( "self_intersections_example.cpp" )
@@ -98,7 +100,6 @@ create_single_source_cgal_program( "corefinement_LCC.cpp")
create_single_source_cgal_program( "hole_filling_example_LCC.cpp" )
create_single_source_cgal_program( "detect_features_example.cpp" )
create_single_source_cgal_program( "manifoldness_repair_example.cpp" )
-create_single_source_cgal_program( "mesh_smoothing_example.cpp")
create_single_source_cgal_program( "repair_polygon_soup_example.cpp" )
set(SuiteSparse_USE_LAPACK_BLAS ON)
diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/mesh_smoothing_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/mesh_smoothing_example.cpp
index 87f5704476c..56c3f8e9c68 100644
--- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/mesh_smoothing_example.cpp
+++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/mesh_smoothing_example.cpp
@@ -1,6 +1,3 @@
-#define CGAL_PMP_SMOOTHING_VERBOSE
-#define CGAL_PMP_SMOOTHING_DEBUG
-
#include
#include
@@ -16,7 +13,7 @@ namespace PMP = CGAL::Polygon_mesh_processing;
int main(int argc, char** argv)
{
- const char* filename = argc > 1 ? argv[1] : "data/grid.off";
+ const char* filename = argc > 1 ? argv[1] : "data/mech-holes-shark.off";
std::ifstream input(filename);
Mesh mesh;
@@ -26,27 +23,27 @@ int main(int argc, char** argv)
return EXIT_FAILURE;
}
- const unsigned int repeat = 1;
- const unsigned int nb_iterations = 50;
-
- for(unsigned int t=0 ; t constrained_vertices;
+ for(Mesh::Vertex_index v : vertices(mesh))
{
-#if 0
-// std::cout << "Smooth areas..." << std::endl;
-// PMP::smooth_areas(mesh, PMP::parameters::number_of_iterations(nb_iterations)
-// .use_safety_constraints(false));
-
- std::cout << "Smooth angles..." << std::endl;
- PMP::smooth_angles(mesh, PMP::parameters::number_of_iterations(nb_iterations)
- .use_safety_constraints(false));
-#else
- PMP::smooth(mesh, PMP::parameters::number_of_iterations(nb_iterations)
- .use_safety_constraints(false));
-#endif
+ if(is_border(v, mesh))
+ constrained_vertices.insert(v);
}
+ std::cout << "Constraining: " << constrained_vertices.size() << " border vertices" << std::endl;
+
+ const unsigned int nb_iterations = 5;
+ CGAL::Boolean_property_map > vcmap(constrained_vertices);
+
+ std::cout << "Smoothing... (" << nb_iterations << " iterations)" << std::endl;
+ PMP::smooth(mesh, PMP::parameters::number_of_iterations(nb_iterations)
+ .use_safety_constraints(false) // authorize all moves
+ .vertex_is_constrained_map(vcmap));
+
std::ofstream output("mesh_smoothed.off");
output << mesh;
+ std::cout << "Done!" << std::endl;
+
return EXIT_SUCCESS;
}
diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/mesh_smoothing_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/mesh_smoothing_impl.h
index f17d0e725d8..49d40f3975b 100644
--- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/mesh_smoothing_impl.h
+++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Smoothing/mesh_smoothing_impl.h
@@ -431,7 +431,7 @@ public:
}
ceres::Solver::Options options;
- options.minimizer_progress_to_stdout = true;
+// options.minimizer_progress_to_stdout = true;
ceres::Solver::Summary summary;
ceres::Solve(options, &problem, &summary);
// std::cout << summary.BriefReport() << "\n";
@@ -462,16 +462,6 @@ class Mesh_smoother
typedef typename boost::property_traits::reference Point_ref;
typedef typename GeomTraits::FT FT;
typedef typename GeomTraits::Vector_3 Vector;
- typedef typename GeomTraits::Segment_3 Segment;
- typedef typename GeomTraits::Triangle_3 Triangle;
-
- typedef std::vector Triangle_list;
- typedef std::pair He_pair;
-
- typedef std::vector Triangle_container;
- typedef CGAL::AABB_triangle_primitive AABB_Primitive;
- typedef CGAL::AABB_traits AABB_Traits;
- typedef CGAL::AABB_tree Tree;
public:
Mesh_smoother(TriangleMesh& pmesh,
@@ -482,8 +472,6 @@ public:
mesh_(pmesh), vpmap_(vpmap), vcmap_(vcmap), traits_(traits)
{}
- ~Mesh_smoother() { delete tree_ptr_; }
-
public:
template
void set_vertex_range(const FaceRange& face_range)
@@ -511,20 +499,6 @@ public:
CGAL_precondition(degen_faces.empty());
set_vertex_range(face_range);
-
- input_triangles_.clear();
- input_triangles_.reserve(face_range.size());
-
- for(face_descriptor f : face_range)
- {
- halfedge_descriptor h = halfedge(f, mesh_);
- input_triangles_.push_back(traits_.construct_triangle_3_object()(get(vpmap_, source(h, mesh_)),
- get(vpmap_, target(h, mesh_)),
- get(vpmap_, target(next(h, mesh_), mesh_))));
- }
-
- tree_ptr_ = new Tree(input_triangles_.begin(), input_triangles_.end());
- tree_ptr_->accelerate_distance_queries();
}
// generic optimizer, the move is computed by 'Optimizer'
@@ -611,7 +585,8 @@ public:
return moved_points;
}
- void project_to_surface()
+ template
+ void project_to_surface(const AABBTree& tree)
{
#ifdef CGAL_PMP_SMOOTHING_DEBUG
std::cout << "Projecting back to the surface" << std::endl;
@@ -623,7 +598,7 @@ public:
continue;
Point_ref p_query = get(vpmap_, v);
- const Point projected = tree_ptr_->closest_point(p_query);
+ const Point projected = tree.closest_point(p_query);
put(vpmap_, v, projected);
}
}
@@ -724,9 +699,7 @@ private:
VertexConstraintMap vcmap_;
GeomTraits traits_;
- Tree* tree_ptr_;
std::vector vrange_;
- Triangle_container input_triangles_;
};
} // namespace internal
diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/smooth_mesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/smooth_mesh.h
index a2c7e78d603..3f10cb96b3d 100644
--- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/smooth_mesh.h
+++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/smooth_mesh.h
@@ -39,124 +39,18 @@ namespace Polygon_mesh_processing {
/*!
* \ingroup PMP_meshing_grp
-* smoothes a triangulated region of a polygon mesh using angle-based criteria.
-* This function improves the angles of triangle faces by iteratively moving non-constrained vertices.
-* Optionally, the points are reprojected to the input surface after each iteration.
*
-* @tparam TriangleMesh model of `MutableFaceGraph`.
-* @tparam FaceRange range of `boost::graph_traits::%face_descriptor`,
- model of `Range`. Its iterator type is `ForwardIterator`.
-* @tparam NamedParameters a sequence of \ref pmp_namedparameters "Named Parameters".
+* \short smoothes a triangulated region of a polygon mesh.
*
-* @param tmesh a polygon mesh with triangulated surface patches to be smoothed.
-* @param faces the range of triangular faces defining one or several surface patches to be smoothed.
-* @param np optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below.
+* This function attempts to make the triangle angle and area distributions as uniform as possible
+* by moving (non-constrained) vertices.
*
-* \cgalNamedParamsBegin
-* \cgalParamBegin{number_of_iterations} the number of iterations for the
-* sequence of the smoothing iterations performed (default is 1).
-* \cgalParamEnd
-* \cgalParamBegin{vertex_is_constrained_map} a property map containing the
-* constrained-or-not status of each vertex of `tmesh`. A constrained vertex
-* cannot be modified at all during smoothing.
-* \cgalParamEnd
-* \cgalParamBegin{do_project} if `true` (default value), points are projected to the initial surface
-* after each iteration.
-* \cgalParamEnd
-* \cgalParamBegin{vertex_point_map} the property map with the points associated
-* to the vertices of `tmesh`. Instance of a class model of `ReadWritePropertyMap`.
-* \cgalParamEnd
-* \cgalParamBegin{geom_traits} a geometric traits class instance, model of `Kernel`.
-* Exact constructions kernels are not supported by this function.
-* \cgalParamEnd
-* \cgalNamedParamsEnd
+* Angle-based smoothing does not change the combinatorial information of the mesh. Area-based smoothing
+* might change the combinatorial information, unless specified otherwise. It is also possible
+* to make the smoothing algorithm "safer" by rejecting moves that, when applied, would worsen the
+* quality of the mesh, e.g. that would decrease the value of the smallest angle around a vertex or
+* create self-intersections.
*
-* @pre `tmesh` does not contain any degenerate faces
-*/
-template
-void smooth_angles(const FaceRange& faces,
- TriangleMesh& tmesh,
- const NamedParameters& np)
-{
- typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor;
-
- typedef typename GetGeomTraits::type GeomTraits;
- typedef typename GetVertexPointMap::type VertexPointMap;
- typedef typename boost::lookup_named_param_def // default
- > ::type VCMap;
-
- typedef internal::Angle_smoother Angle_optimizer;
- typedef internal::Mesh_smoother Angle_smoother;
-
- if(std::begin(faces) == std::end(faces))
- return;
-
- using boost::choose_param;
- using boost::get_param;
-
- // named parameters
- GeomTraits gt = choose_param(get_param(np, internal_np::geom_traits),
- GeomTraits());
- VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
- get_property_map(CGAL::vertex_point, tmesh));
- VCMap vcmap = choose_param(get_param(np, internal_np::vertex_is_constrained),
- Constant_property_map());
-
- std::size_t nb_iterations = choose_param(get_param(np, internal_np::number_of_iterations), 1);
- const bool do_project = choose_param(get_param(np, internal_np::do_project), true);
- const bool use_safety_constraints = choose_param(get_param(np, internal_np::use_safety_constraints), true);
-
- Angle_smoother smoother(tmesh, vpmap, vcmap, gt);
-
- smoother.init_smoothing(faces);
-
- for(std::size_t i=0; i
-void smooth_angles(const FaceRange& face_range, TriangleMesh& tmesh)
-{
- smooth_angles(face_range, tmesh, parameters::all_default());
-}
-
-template
-void smooth_angles(TriangleMesh& tmesh, const CGAL_PMP_NP_CLASS& np)
-{
- smooth_angles(faces(tmesh), tmesh, np);
-}
-
-template
-void smooth_angles(TriangleMesh& tmesh)
-{
- smooth_angles(faces(tmesh), tmesh, parameters::all_default());
-}
-
-/*!
-* \ingroup PMP_meshing_grp
-* smoothes a triangulated region of a polygon mesh using area-based criteria.
-* This function tries to make the triangle area distribution as uniform as possible
-* by moving non-constrained vertices.
* Optionally, the points are reprojected after each iteration.
*
* @tparam TriangleMesh model of `MutableFaceGraph`.
@@ -169,131 +63,52 @@ void smooth_angles(TriangleMesh& tmesh)
* @param np optional sequence of \ref pmp_namedparameters "Named Parameters" among the ones listed below.
*
* \cgalNamedParamsBegin
+* \cgalParamBegin{use_angle_based_smoothing} Boolean value to indicate whether angle-based smoothing should be used.
+* Default is `true`.
+* \cgalParamEnd
+* \cgalParamBegin{use_area_based_smoothing} Boolean value to indicate whether area-based smoothing should be used.
+* Default is `true`.
+* \cgalParamEnd
+* \cgalParamBegin{number_of_iterations} the number of iterations for the
+* sequence of the smoothing iterations performed (default is 1).
+* \cgalParamEnd
+* \cgalParamBegin{use_safety_constraints} if `true`, vertex moves that would worsen the mesh
+* are ignored. Default is `false`.
+* \cgalParamEnd
+* \cgalParamBegin{use_Delaunay_flips} if `true` (default value), area-based smoothing will be completed
+* by a phase of Delaunay-based edge-flips to prevent the creation of elongated triangles.
+* \cgalParamEnd
+* \cgalParamBegin{do_project} if `true` (default value), points are projected onto the initial surface
+* after each iteration.
+* \cgalParamEnd
+* \cgalParamBegin{vertex_is_constrained_map} a property map containing the
+* constrained-or-not status of each vertex of `tmesh`. A constrained vertex
+* cannot be modified at all during smoothing.
+* \cgalParamEnd
+* \cgalParamBegin{edge_is_constrained_map} a property map containing the
+* constrained-or-not status of each edge of `tmesh`. A constrained edge cannot be flipped.
+* \cgalParamEnd
* \cgalParamBegin{vertex_point_map} the property map with the points associated
* to the vertices of `tmesh`. Instance of a class model of `ReadWritePropertyMap`.
* \cgalParamEnd
* \cgalParamBegin{geom_traits} a geometric traits class instance, model of `Kernel`.
* Exact constructions kernels are not supported by this function.
* \cgalParamEnd
-* \cgalParamBegin{number_of_iterations} the number of iterations for the
-* sequence of the smoothing iterations performed (default is 1).
-* \cgalParamEnd
-* \cgalParamBegin{vertex_is_constrained_map} a property map containing the
-* constrained-or-not status of each vertex of `tmesh`. A constrained vertex
-* cannot be modified at all during smoothing.
-* \cgalParamEnd
-* \cgalParamBegin{do_project} if `true` (default value), points are projected to the initial surface after each iteration.
-* \cgalParamEnd
* \cgalNamedParamsEnd
*
+* @warning The third party libraries Ceres (and Eigen) are required to use the area-based smoothing.
+*
* @pre `tmesh` does not contain any degenerate faces
*/
template
-void smooth_areas(const FaceRange faces,
- TriangleMesh& tmesh,
- const NamedParameters& np)
-{
- typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor;
- typedef typename boost::graph_traits::edge_descriptor edge_descriptor;
-
- typedef typename GetGeomTraits::type GeomTraits;
- typedef typename GetVertexPointMap::type VertexPointMap;
-
- typedef typename boost::lookup_named_param_def // default
- > ::type VCMap;
- typedef typename boost::lookup_named_param_def // default
- > ::type ECMap;
-
- typedef internal::Area_smoother Area_optimizer;
- typedef internal::Mesh_smoother Area_smoother;
-
- typedef internal::Delaunay_edge_flipper Delaunay_flipper;
-
- if(std::begin(faces) == std::end(faces))
- return;
-
- using boost::choose_param;
- using boost::get_param;
-
- // named parameters
- GeomTraits gt = choose_param(get_param(np, internal_np::geom_traits),
- GeomTraits());
- VertexPointMap vpmap = choose_param(get_param(np, internal_np::vertex_point),
- get_property_map(CGAL::vertex_point, tmesh));
- VCMap vcmap = choose_param(get_param(np, internal_np::vertex_is_constrained),
- Constant_property_map());
-
- std::size_t nb_iterations = choose_param(get_param(np, internal_np::number_of_iterations), 1);
- const bool do_project = choose_param(get_param(np, internal_np::do_project), true);
- const bool use_safety_constraints = choose_param(get_param(np, internal_np::use_safety_constraints), true);
- const bool use_Delaunay_flips = choose_param(get_param(np, internal_np::use_delaunay_triangulation), true);
-
- Area_smoother smoother(tmesh, vpmap, vcmap, gt);
-
- smoother.init_smoothing(faces);
-
- for(std::size_t i=0; i(false));
-
- Delaunay_flipper delaunay_flipper(tmesh, vpmap, ecmap, gt);
- delaunay_flipper(faces);
- }
-}
-
-template
-void smooth_areas(const FaceRange& face_range, TriangleMesh& tmesh)
-{
- smooth_areas(face_range, tmesh, parameters::all_default());
-}
-
-template
-void smooth_areas(TriangleMesh& tmesh, const CGAL_PMP_NP_CLASS& np)
-{
- smooth_areas(faces(tmesh), tmesh, np);
-}
-
-template
-void smooth_areas(TriangleMesh& tmesh)
-{
- smooth_areas(faces(tmesh), tmesh, parameters::all_default());
-}
-
-// do both
-template
void smooth(const FaceRange& faces,
TriangleMesh& tmesh,
const NamedParameters& np)
{
typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor;
+ typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits::edge_descriptor edge_descriptor;
+ typedef typename boost::graph_traits::face_descriptor face_descriptor;
typedef typename GetGeomTraits::type GeomTraits;
typedef typename GetVertexPointMap::type VertexPointMap;
@@ -317,6 +132,14 @@ void smooth(const FaceRange& faces,
typedef internal::Mesh_smoother Angle_smoother;
+ typedef typename GeomTraits::Triangle_3 Triangle;
+ typedef std::vector Triangle_container;
+
+ typedef CGAL::AABB_triangle_primitive AABB_Primitive;
+ typedef CGAL::AABB_traits AABB_Traits;
+ typedef CGAL::AABB_tree Tree;
+
if(std::begin(faces) == std::end(faces))
return;
@@ -331,66 +154,100 @@ void smooth(const FaceRange& faces,
VCMap vcmap = choose_param(get_param(np, internal_np::vertex_is_constrained),
Constant_property_map());
+ const bool use_angle_smoothing = choose_param(get_param(np, internal_np::use_angle_smoothing), true);
+ const bool use_area_smoothing = choose_param(get_param(np, internal_np::use_area_smoothing), true);
+
+ if(!use_angle_smoothing && !use_area_smoothing)
+ std::cerr << "Warning: called PMP::smooth() but no smoothing method is being used" << std::endl;
+
std::size_t nb_iterations = choose_param(get_param(np, internal_np::number_of_iterations), 1);
const bool do_project = choose_param(get_param(np, internal_np::do_project), true);
const bool use_safety_constraints = choose_param(get_param(np, internal_np::use_safety_constraints), true);
- const bool use_Delaunay_flips = choose_param(get_param(np, internal_np::use_delaunay_triangulation), false);
+ const bool use_Delaunay_flips = choose_param(get_param(np, internal_np::use_Delaunay_flips), false);
+ // Construct the AABB tree (if needed for reprojection)
+ std::vector input_triangles;
+
+ if(do_project)
+ {
+ input_triangles.reserve(faces.size());
+
+ for(face_descriptor f : faces)
+ {
+ halfedge_descriptor h = halfedge(f, tmesh);
+ input_triangles.push_back(gt.construct_triangle_3_object()(get(vpmap, source(h, tmesh)),
+ get(vpmap, target(h, tmesh)),
+ get(vpmap, target(next(h, tmesh), tmesh))));
+ }
+ }
+
+ Tree aabb_tree(input_triangles.begin(), input_triangles.end());
+ aabb_tree.accelerate_distance_queries();
+
+ // Setup the working ranges and check some preconditions
Area_smoother area_smoother(tmesh, vpmap, vcmap, gt);
Angle_smoother angle_smoother(tmesh, vpmap, vcmap, gt);
- area_smoother.init_smoothing(faces);
- angle_smoother.init_smoothing(faces);
+ if(use_area_smoothing)
+ area_smoother.init_smoothing(faces);
+
+ if(use_angle_smoothing)
+ angle_smoother.init_smoothing(faces);
for(std::size_t i=0; i(false));
- if(use_Delaunay_flips)
- {
- ECMap ecmap = choose_param(get_param(np, internal_np::edge_is_constrained),
- Constant_property_map(false));
-
- Delaunay_flipper delaunay_flipper(tmesh, vpmap, ecmap, gt);
- delaunay_flipper(faces);
+ Delaunay_flipper delaunay_flipper(tmesh, vpmap, ecmap, gt);
+ delaunay_flipper(faces);
+ }
}
// ... then angle smoothing
- angle_smoother.optimize(use_safety_constraints /*check for bad faces*/,
- true /*apply all moves at once*/,
- use_safety_constraints /*check if the min angle is improved*/);
-
- if(do_project)
+ if(use_angle_smoothing)
{
- if(use_safety_constraints && does_self_intersect(tmesh))
- {
-#ifdef CGAL_PMP_SMOOTHING_VERBOSE
- std::cerr << "Can't do re-projection, there are self-intersections in the mesh!\n";
-#endif
- break;
- }
+ angle_smoother.optimize(use_safety_constraints /*check for bad faces*/,
+ true /*apply all moves at once*/,
+ use_safety_constraints /*check if the min angle is improved*/);
- angle_smoother.project_to_surface();
+ if(do_project)
+ {
+ if(use_safety_constraints && does_self_intersect(tmesh))
+ {
+#ifdef CGAL_PMP_SMOOTHING_VERBOSE
+ std::cerr << "Can't do re-projection, there are self-intersections in the mesh!\n";
+#endif
+ break;
+ }
+
+ angle_smoother.project_to_surface(aabb_tree);
+ }
}
}
}
-
template
void smooth(const FaceRange& face_range, TriangleMesh& tmesh)
{
diff --git a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_mesh_smoothing.cpp b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_mesh_smoothing.cpp
index 8b3a092bd18..eeeb0d2acd0 100644
--- a/Polygon_mesh_processing/test/Polygon_mesh_processing/test_mesh_smoothing.cpp
+++ b/Polygon_mesh_processing/test/Polygon_mesh_processing/test_mesh_smoothing.cpp
@@ -19,8 +19,7 @@ typedef CGAL::Polyhedron_3 Polyhedron;
namespace PMP = CGAL::Polygon_mesh_processing;
template
-void read_mesh(const char* filename,
- Mesh& mesh)
+void read_mesh(const char* filename, Mesh& mesh)
{
std::ifstream input(filename);
@@ -47,8 +46,9 @@ void test_angle_smoothing(const char* filename)
Mesh mesh;
read_mesh(filename, mesh);
- PMP::smooth_angles(mesh);
- PMP::smooth_angles(mesh, CGAL::parameters::number_of_iterations(10));
+ PMP::smooth(mesh);
+ PMP::smooth(mesh, CGAL::parameters::number_of_iterations(10)
+ .use_area_smoothing(false));
}
template
@@ -57,8 +57,9 @@ void test_area_smoothing(const char* filename)
Mesh mesh;
read_mesh(filename, mesh);
- PMP::smooth_areas(mesh);
- PMP::smooth_areas(mesh, CGAL::parameters::number_of_iterations(10));
+ PMP::smooth(mesh);
+ PMP::smooth(mesh, CGAL::parameters::number_of_iterations(10)
+ .use_angle_smoothing(false));
}
template
@@ -67,7 +68,8 @@ void test_angle_smoothing_without_projection(const char* filename)
Mesh mesh;
read_mesh(filename, mesh);
- PMP::smooth_angles(mesh, CGAL::parameters::do_project(false));
+ PMP::smooth(mesh, CGAL::parameters::do_project(false)
+ .use_area_smoothing(false));
}
template
@@ -76,7 +78,8 @@ void test_area_smoothing_without_projection(const char* filename)
Mesh mesh;
read_mesh(filename, mesh);
- PMP::smooth_areas(mesh, CGAL::parameters::do_project(false));
+ PMP::smooth(mesh, CGAL::parameters::do_project(false)
+ .use_angle_smoothing(false));
}
template
@@ -88,32 +91,25 @@ void test_constrained_vertices(const char* filename)
typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor;
typename boost::property_map::type vpmap = get(CGAL::vertex_point, mesh);
- double x_init, y_init, z_init;
std::set selected_vertices;
+ std::map initial_positions;
for(vertex_descriptor v : vertices(mesh))
{
if(!is_border(v, mesh))
{
selected_vertices.insert(v);
- x_init = get(vpmap, v).x();
- y_init = get(vpmap, v).y();
- z_init = get(vpmap, v).z();
+ initial_positions[v] = get(vpmap, v);
}
}
CGAL::Boolean_property_map > vcmap(selected_vertices);
- PMP::smooth_angles(mesh, CGAL::parameters::vertex_is_constrained_map(vcmap));
- PMP::smooth_areas(mesh, CGAL::parameters::vertex_is_constrained_map(vcmap));
+ PMP::smooth(mesh, CGAL::parameters::vertex_is_constrained_map(vcmap));
for(vertex_descriptor v : vertices(mesh))
{
if(!is_border(v, mesh))
- {
- assert(x_init == get(vpmap, v).x());
- assert(y_init == get(vpmap, v).y());
- assert(z_init == get(vpmap, v).z());
- }
+ assert(initial_positions.at(v) == get(vpmap, v));
}
}
diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp
index a57ae16547e..2d67982bf67 100644
--- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp
+++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp
@@ -191,19 +191,21 @@ public Q_SLOTS:
}
else
{
- smooth_angles(pmesh, parameters::do_project(projection)
- .number_of_iterations(nb_iter)
- .use_safety_constraints(use_safety_measures)
- .vertex_is_constrained_map(vcmap));
+ smooth(pmesh, parameters::do_project(projection)
+ .number_of_iterations(nb_iter)
+ .use_safety_constraints(use_safety_measures)
+ .vertex_is_constrained_map(vcmap)
+ .use_area_smoothing(false));
}
}
else if(use_area_smoothing)
{
- smooth_areas(pmesh, parameters::do_project(projection)
- .number_of_iterations(nb_iter)
- .use_delaunay_triangulation(use_Delaunay_flips)
- .use_safety_constraints(use_safety_measures)
- .vertex_is_constrained_map(vcmap));
+ smooth(pmesh, parameters::do_project(projection)
+ .number_of_iterations(nb_iter)
+ .use_delaunay_triangulation(use_Delaunay_flips)
+ .use_safety_constraints(use_safety_measures)
+ .vertex_is_constrained_map(vcmap)
+ .use_angle_smoothing(false));
}
poly_item->invalidateOpenGLBuffers();
@@ -219,22 +221,28 @@ public Q_SLOTS:
if(use_angle_smoothing)
{
if(use_area_smoothing)
+ {
smooth(pmesh, parameters::do_project(projection)
.number_of_iterations(nb_iter)
.use_safety_constraints(use_safety_measures)
.vertex_is_constrained_map(vcmap));
+ }
else
- smooth_angles(pmesh, parameters::do_project(projection)
- .number_of_iterations(nb_iter)
- .use_safety_constraints(use_safety_measures)
- .vertex_is_constrained_map(vcmap));
+ {
+ smooth(pmesh, parameters::do_project(projection)
+ .number_of_iterations(nb_iter)
+ .use_safety_constraints(use_safety_measures)
+ .vertex_is_constrained_map(vcmap)
+ .use_area_smoothing(false));
+ }
}
else
{
- smooth_areas(pmesh, parameters::do_project(projection)
- .number_of_iterations(nb_iter)
- .use_safety_constraints(use_safety_measures)
- .vertex_is_constrained_map(vcmap));
+ smooth(pmesh, parameters::do_project(projection)
+ .number_of_iterations(nb_iter)
+ .use_safety_constraints(use_safety_measures)
+ .vertex_is_constrained_map(vcmap)
+ .use_angle_smoothing(false));
}
}
else // some faces exist in the selection
@@ -250,18 +258,20 @@ public Q_SLOTS:
}
else
{
- smooth_angles(selection_item->selected_facets, pmesh, parameters::do_project(projection)
- .number_of_iterations(nb_iter)
- .use_safety_constraints(use_safety_measures)
- .vertex_is_constrained_map(vcmap));
+ smooth(selection_item->selected_facets, pmesh, parameters::do_project(projection)
+ .number_of_iterations(nb_iter)
+ .use_safety_constraints(use_safety_measures)
+ .vertex_is_constrained_map(vcmap)
+ .use_area_smoothing(false));
}
}
else
{
- smooth_areas(selection_item->selected_facets, pmesh, parameters::do_project(projection)
- .number_of_iterations(nb_iter)
- .use_safety_constraints(use_safety_measures)
- .vertex_is_constrained_map(vcmap));
+ smooth(selection_item->selected_facets, pmesh, parameters::do_project(projection)
+ .number_of_iterations(nb_iter)
+ .use_safety_constraints(use_safety_measures)
+ .vertex_is_constrained_map(vcmap)
+ .use_angle_smoothing(false));
}
}