Merge smooth_areas and smooth_angles, rework AABB tree, document everything

This commit is contained in:
Mael Rouxel-Labbé 2019-06-18 16:02:42 +02:00
parent 9942669dd1
commit 22bd8a943b
12 changed files with 255 additions and 368 deletions

View File

@ -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

View File

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

View File

@ -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.

View File

@ -391,11 +391,52 @@ if orientation should matter when determining whether two faces are duplicates.
<b>Default:</b> `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
<b>Type:</b> `bool` \n
<b>Default:</b> `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
<b>Type:</b> `bool` \n
<b>Default:</b> `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
<b>Type:</b> `bool` \n
<b>Default:</b> `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
<b>Type:</b> `bool` \n
<b>Default:</b> `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
<b>Type:</b> `bool` \n

View File

@ -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()`

View File

@ -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

View File

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

View File

@ -1,6 +1,3 @@
#define CGAL_PMP_SMOOTHING_VERBOSE
#define CGAL_PMP_SMOOTHING_DEBUG
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
@ -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<repeat; ++t)
std::set<Mesh::Vertex_index> 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<std::set<Mesh::Vertex_index> > 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;
}

View File

@ -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<VertexPointMap>::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> Triangle_list;
typedef std::pair<halfedge_descriptor, halfedge_descriptor> He_pair;
typedef std::vector<Triangle> Triangle_container;
typedef CGAL::AABB_triangle_primitive<GeomTraits, typename Triangle_container::iterator> AABB_Primitive;
typedef CGAL::AABB_traits<GeomTraits, AABB_Primitive> AABB_Traits;
typedef CGAL::AABB_tree<AABB_Traits> 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<typename FaceRange>
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 <typename AABBTree>
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<vertex_descriptor> vrange_;
Triangle_container input_triangles_;
};
} // namespace internal

View File

@ -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<TriangleMesh>::%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<typename TriangleMesh, typename FaceRange, typename NamedParameters>
void smooth_angles(const FaceRange& faces,
TriangleMesh& tmesh,
const NamedParameters& np)
{
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename GetGeomTraits<TriangleMesh, NamedParameters>::type GeomTraits;
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::type VertexPointMap;
typedef typename boost::lookup_named_param_def<internal_np::vertex_is_constrained_t,
NamedParameters,
Constant_property_map<vertex_descriptor, bool> // default
> ::type VCMap;
typedef internal::Angle_smoother<TriangleMesh, VertexPointMap, GeomTraits> Angle_optimizer;
typedef internal::Mesh_smoother<Angle_optimizer, TriangleMesh,
VertexPointMap, VCMap, GeomTraits> 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<vertex_descriptor, bool>());
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<nb_iterations; ++i)
{
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_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;
}
smoother.project_to_surface();
}
}
}
template <typename FaceRange, typename TriangleMesh>
void smooth_angles(const FaceRange& face_range, TriangleMesh& tmesh)
{
smooth_angles(face_range, tmesh, parameters::all_default());
}
template <typename TriangleMesh, typename CGAL_PMP_NP_TEMPLATE_PARAMETERS>
void smooth_angles(TriangleMesh& tmesh, const CGAL_PMP_NP_CLASS& np)
{
smooth_angles(faces(tmesh), tmesh, np);
}
template<typename TriangleMesh>
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<typename TriangleMesh, typename FaceRange, typename NamedParameters>
void smooth_areas(const FaceRange faces,
TriangleMesh& tmesh,
const NamedParameters& np)
{
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::edge_descriptor edge_descriptor;
typedef typename GetGeomTraits<TriangleMesh, NamedParameters>::type GeomTraits;
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::type VertexPointMap;
typedef typename boost::lookup_named_param_def<internal_np::vertex_is_constrained_t,
NamedParameters,
Constant_property_map<vertex_descriptor, bool> // default
> ::type VCMap;
typedef typename boost::lookup_named_param_def<internal_np::edge_is_constrained_t,
NamedParameters,
Constant_property_map<edge_descriptor, bool> // default
> ::type ECMap;
typedef internal::Area_smoother<TriangleMesh, VertexPointMap, GeomTraits> Area_optimizer;
typedef internal::Mesh_smoother<Area_optimizer, TriangleMesh,
VertexPointMap, VCMap, GeomTraits> Area_smoother;
typedef internal::Delaunay_edge_flipper<TriangleMesh, VertexPointMap,
ECMap, GeomTraits> 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<vertex_descriptor, bool>());
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<nb_iterations; ++i)
{
smoother.optimize(use_safety_constraints /*check for bad faces*/,
false /*apply moves as soon as they're calculated*/,
false /*do not enforce a minimum angle improvement*/);
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;
}
smoother.project_to_surface();
}
}
if(use_Delaunay_flips)
{
ECMap ecmap = choose_param(get_param(np, internal_np::edge_is_constrained),
Constant_property_map<edge_descriptor, bool>(false));
Delaunay_flipper delaunay_flipper(tmesh, vpmap, ecmap, gt);
delaunay_flipper(faces);
}
}
template <typename FaceRange, typename TriangleMesh>
void smooth_areas(const FaceRange& face_range, TriangleMesh& tmesh)
{
smooth_areas(face_range, tmesh, parameters::all_default());
}
template <typename TriangleMesh, typename CGAL_PMP_NP_TEMPLATE_PARAMETERS>
void smooth_areas(TriangleMesh& tmesh, const CGAL_PMP_NP_CLASS& np)
{
smooth_areas(faces(tmesh), tmesh, np);
}
template<typename TriangleMesh>
void smooth_areas(TriangleMesh& tmesh)
{
smooth_areas(faces(tmesh), tmesh, parameters::all_default());
}
// do both
template<typename TriangleMesh, typename FaceRange, typename NamedParameters>
void smooth(const FaceRange& faces,
TriangleMesh& tmesh,
const NamedParameters& np)
{
typedef typename boost::graph_traits<TriangleMesh>::vertex_descriptor vertex_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::edge_descriptor edge_descriptor;
typedef typename boost::graph_traits<TriangleMesh>::face_descriptor face_descriptor;
typedef typename GetGeomTraits<TriangleMesh, NamedParameters>::type GeomTraits;
typedef typename GetVertexPointMap<TriangleMesh, NamedParameters>::type VertexPointMap;
@ -317,6 +132,14 @@ void smooth(const FaceRange& faces,
typedef internal::Mesh_smoother<Angle_optimizer, TriangleMesh,
VertexPointMap, VCMap, GeomTraits> Angle_smoother;
typedef typename GeomTraits::Triangle_3 Triangle;
typedef std::vector<Triangle> Triangle_container;
typedef CGAL::AABB_triangle_primitive<GeomTraits,
typename Triangle_container::iterator> AABB_Primitive;
typedef CGAL::AABB_traits<GeomTraits, AABB_Primitive> AABB_Traits;
typedef CGAL::AABB_tree<AABB_Traits> 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<vertex_descriptor, bool>());
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<Triangle> 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<nb_iterations; ++i)
{
// First apply area smoothing...
area_smoother.optimize(use_safety_constraints /*check for bad faces*/,
false /*apply moves as soon as they're calculated*/,
false /*do not enforce a minimum angle improvement*/);
if(do_project)
if(use_area_smoothing)
{
if(use_safety_constraints && does_self_intersect(tmesh))
// First apply area smoothing...
area_smoother.optimize(use_safety_constraints /*check for bad faces*/,
false /*apply moves as soon as they're calculated*/,
false /*do not enforce a minimum angle improvement*/);
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";
std::cerr << "Cannot re-project as there are self-intersections in the mesh!\n";
#endif
break;
break;
}
area_smoother.project_to_surface(aabb_tree);
}
area_smoother.project_to_surface();
}
if(use_Delaunay_flips)
{
ECMap ecmap = choose_param(get_param(np, internal_np::edge_is_constrained),
Constant_property_map<edge_descriptor, bool>(false));
if(use_Delaunay_flips)
{
ECMap ecmap = choose_param(get_param(np, internal_np::edge_is_constrained),
Constant_property_map<edge_descriptor, bool>(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 <typename FaceRange, typename TriangleMesh>
void smooth(const FaceRange& face_range, TriangleMesh& tmesh)
{

View File

@ -19,8 +19,7 @@ typedef CGAL::Polyhedron_3<Kernel> Polyhedron;
namespace PMP = CGAL::Polygon_mesh_processing;
template <typename Mesh>
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 <typename Mesh>
@ -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 <typename Mesh>
@ -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 <typename Mesh>
@ -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 <typename Mesh>
@ -88,32 +91,25 @@ void test_constrained_vertices(const char* filename)
typedef typename boost::graph_traits<Mesh>::vertex_descriptor vertex_descriptor;
typename boost::property_map<Mesh, CGAL::vertex_point_t>::type vpmap = get(CGAL::vertex_point, mesh);
double x_init, y_init, z_init;
std::set<vertex_descriptor> selected_vertices;
std::map<vertex_descriptor, Point> 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<std::set<vertex_descriptor> > 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));
}
}

View File

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