From b26818c0236e5d92994980ca16b36cc9c1889b27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 10 Sep 2021 18:13:53 +0200 Subject: [PATCH 01/46] add first version of tangential_relation extracted from isotropic_remeshing --- .../tangential_relaxation.h | 280 ++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h new file mode 100644 index 00000000000..4b0c53e23da --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -0,0 +1,280 @@ +// Copyright (c) 2015-2021 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Jane Tournois + + +#ifndef CGAL_POLYGON_MESH_PROCESSING_TANGENTIAL_RELAXATION_H +#define CGAL_POLYGON_MESH_PROCESSING_TANGENTIAL_RELAXATION_H + +#include + +#include +#include + +#include +#include + +#include +#include + +namespace CGAL { +namespace Polygon_mesh_processing { + +/*! +* \ingroup PMP_meshing_grp +* \todo rephrase +* applies an iterative smoothing filter to the mesh. +* The vertex movement has to be constrained to the vertex tangent plane [...] +* smoothing algorithm with uniform Laplacian weights +* +* @tparam Triangle model of `MutableFaceGraph`. +* The descriptor types `boost::graph_traits::%face_descriptor` +* and `boost::graph_traits::%halfedge_descriptor` must be +* models of `Hashable`. +* @tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" +* +* @param tm a triangle mesh +* @param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below +* +* \cgalNamedParamsBegin +* \cgalParamNBegin{vertex_point_map} +* \cgalParamDescription{a property map associating points to the vertices of `tm`} +* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%vertex_descriptor` +* as key type and `%Point_3` as value type} +* \cgalParamDefault{`boost::get(CGAL::vertex_point, tm)`} +* \cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t` +* must be available in `PolygonMesh`.} +* \cgalParamNEnd +* +* \cgalParamNBegin{geom_traits} +* \cgalParamDescription{an instance of a geometric traits class} +* \cgalParamType{a class model of `Kernel`} +* \cgalParamDefault{a \cgal Kernel deduced from the `Point_3` type, using `CGAL::Kernel_traits`} +* \cgalParamExtra{The geometric traits class must be compatible with the vertex `Point_3` type.} +* \cgalParamExtra{Exact constructions kernels are not supported by this function.} +* \cgalParamNEnd +* +* \cgalParamNBegin{number_of_iterations} +* \cgalParamDescription{the number of iterations for the sequence of atomic operations performed (listed in the above description)} +* \cgalParamType{unsigned int} +* \cgalParamDefault{`1`} +* \cgalParamNEnd +* +* \cgalParamNBegin{edge_is_constrained_map} +* \cgalParamDescription{a property map containing the constrained-or-not status of each edge of `tm`} +* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%edge_descriptor` +* as key type and `bool` as value type. It must be default constructible.} +* \cgalParamDefault{a default property map where no edge is constrained} +* \cgalParamExtra{A constrained edge can be split or collapsed, but not flipped, nor its endpoints moved by smoothing.} +* \cgalParamExtra{Sub-edges generated by splitting are set to be constrained.} +* \cgalParamExtra{Patch boundary edges (i.e. incident to only one face in the range) are always considered as constrained edges.} +* \cgalParamNEnd +* +* \cgalParamNBegin{vertex_is_constrained_map} +* \cgalParamDescription{a property map containing the constrained-or-not status of each vertex of `tm`.} +* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%vertex_descriptor` +* as key type and `bool` as value type. It must be default constructible.} +* \cgalParamDefault{a default property map where no vertex is constrained} +* \cgalParamExtra{A constrained vertex cannot be modified during remeshing.} +* \cgalParamNEnd +* +* \cgalParamNBegin{relax_constraints} +* \cgalParamDescription{If `true`, the end vertices of the edges set as constrained +* in `edge_is_constrained_map` and boundary edges move along the} +* constrained polylines they belong to.} +* \cgalParamType{Boolean} +* \cgalParamDefault{`false`} +* \cgalParamNEnd +* +* \cgalNamedParamsEnd +* +* \todo shall we take a range of vertices as input? +*/ +template +void tangential_relaxation(TriangleMesh& tm, 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; + + using parameters::get_parameter; + using parameters::choose_parameter; + + typedef typename GetGeomTraits::type GT; + GT gt = choose_parameter(get_parameter(np, internal_np::geom_traits)); + + typedef typename GetVertexPointMap::type VPMap; + VPMap vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), + get_property_map(vertex_point, tm)); + + typedef Static_boolean_property_map Default_ECM; + typedef typename internal_np::Lookup_named_param_def < + internal_np::edge_is_constrained_t, + NamedParameters, + Static_boolean_property_map // default (no constraint) + > ::type ECM; + ECM ecm = choose_parameter(get_parameter(np, internal_np::edge_is_constrained), + Default_ECM()); + + typedef typename internal_np::Lookup_named_param_def < + internal_np::vertex_is_constrained_t, + NamedParameters, + Static_boolean_property_map // default (no constraint) + > ::type VCM; + VCM vcm = choose_parameter(get_parameter(np, internal_np::vertex_is_constrained), + Static_boolean_property_map()); + + const bool relax_constraints = choose_parameter(get_parameter(np, internal_np::relax_constraints), false); + const unsigned int nb_iterations = choose_parameter(get_parameter(np, internal_np::number_of_iterations), 1); + + typedef typename GT::Vector_3 Vector_3; + typedef typename GT::Point_3 Point_3; + + auto check_normals = [&](vertex_descriptor v) + { + bool first_run = true; + Vector_3 prev = NULL_VECTOR, first=NULL_VECTOR; + halfedge_descriptor first_h = boost::graph_traits::null_halfedge(); + for(halfedge_descriptor hd : CGAL::halfedges_around_target(v, tm)) + { + if (is_border(hd, tm)) continue; + + Vector_3 n = compute_face_normal(face(hd, tm), tm); + if (n == CGAL::NULL_VECTOR) //for degenerate faces + continue; + + if (first_run) + { + first_run = false; + first=n; + first_h = hd; + } + else + { + if (!get(ecm, edge(hd, tm))) + if( to_double(n * prev) <= 0 ) + return false; + } + prev=n; + } + if (!get(ecm, edge(first_h, tm))) + if( to_double(first * prev) <= 0 ) + return false; + + return true; + }; + + for (unsigned int nit = 0; nit < nb_iterations; ++nit) + { + typedef std::tuple VNP; + std::vector< VNP > barycenters; + // at each vertex, compute vertex normal + // at each vertex, compute barycenter of neighbors + for(vertex_descriptor v : vertices(tm)) + { + if (get(vcm, v) || halfedge(v, tm)==boost::graph_traits::null_halfedge()) + continue; + + std::vector interior_hedges, border_halfedges; + + // collect hedges to detect if we have to handle boundary cases + for(halfedge_descriptor h : halfedges_around_target(v, tm)) + { + if (is_border_edge(h, tm) || get(ecm, edge(h, tm))) + border_halfedges.push_back(h); + else + interior_hedges.push_back(h); + } + + if (border_halfedges.empty()) + { + // \todo: shall we want to have a way to compute once for all vertices (per loop) + // this would avoid recompute face normals + Vector_3 vn = compute_vertex_normal(v, tm, + parameters::vertex_point_map(vpm) + .geom_traits(gt)); + Vector_3 move = CGAL::NULL_VECTOR; + unsigned int star_size = 0; + for(halfedge_descriptor h :interior_hedges) + { + move = move + Vector_3(get(vpm, v), get(vpm, source(h, tm))); + ++star_size; + } + CGAL_assertion(star_size > 0); //isolated vertices have already been discarded + move = (1. / (double)star_size) * move; + + barycenters.push_back( VNP(v, vn, get(vpm, v) + move) ); + } + else + { + if (!relax_constraints) continue; + Vector_3 vn(NULL_VECTOR); + + if (border_halfedges.size() == 2)// corner are constrained + { + vertex_descriptor ph0 = source(border_halfedges[0], tm); + vertex_descriptor ph1 = source(border_halfedges[1], tm); + double dot = to_double(Vector_3(get(vpm, v), get(vpm, ph0)) + * Vector_3(get(vpm, v), get(vpm, ph1))); + // \todo shouldn't it be an input parameter? + //check squared cosine is < 0.25 (~120 degrees) + if (0.25 < dot*dot / ( squared_distance(get(vpm,ph0), get(vpm, v)) * + squared_distance(get(vpm,ph1), get(vpm, v))) ) + barycenters.push_back( VNP(v, vn, barycenter(get(vpm, ph0), 0.25, get(vpm, ph1), 0.25, get(vpm, v), 0.5)) ); + } + } + } + + // compute moves + typedef std::pair VP_pair; + std::vector< std::pair > new_locations; + new_locations.reserve(barycenters.size()); + for(const VNP& vnp : barycenters) + { + vertex_descriptor v = std::get<0>(vnp); + Point_3 pv = get(vpm, v); + const Vector_3& nv = std::get<1>(vnp); + const Point_3& qv = std::get<2>(vnp); //barycenter at v + + new_locations.push_back( std::make_pair(v, qv + (nv * Vector_3(qv, pv)) * nv) ); + } + + // perform moves + for(const VP_pair& vp : new_locations) + { + const Point_3 initial_pos = get(vpm, vp.first); + const Vector_3 move(initial_pos, vp.second); + + put(vpm, vp.first, vp.second); + + //check that no inversion happened + double frac = 1.; + while (frac > 0.03 //5 attempts maximum + && !check_normals(vp.first)) //if a face has been inverted + { + frac = 0.5 * frac; + put(vpm, vp.first, initial_pos + frac * move);//shorten the move by 2 + } + if (frac <= 0.02) + put(vpm, vp.first, initial_pos);//cancel move + } + }//end for loop (nit == nb_iterations) +} + +template +void tangential_relaxation(TriangleMesh& tm) +{ + triangle(tm, parameters::all_default()); +} + +} } // CGAL::Polygon_mesh_processing + +#endif //CGAL_POLYGON_MESH_PROCESSING_TANGENTIAL_RELAXATION_H From ff0425db2e4caf4b86f2a48c6bf0347961d1f445 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Mon, 13 Sep 2021 10:56:30 +0200 Subject: [PATCH 02/46] add minimal example and fix compilation --- .../Polygon_mesh_processing/CMakeLists.txt | 1 + .../tangential_relaxation_example.cpp | 36 +++++++++++++++++++ .../tangential_relaxation.h | 2 +- 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt index 99ebc9a5b6d..685df8986ac 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/CMakeLists.txt @@ -72,6 +72,7 @@ create_single_source_cgal_program( "mesh_slicer_example.cpp") #create_single_source_cgal_program( "remove_degeneracies_example.cpp") create_single_source_cgal_program("isotropic_remeshing_example.cpp") create_single_source_cgal_program("isotropic_remeshing_of_patch_example.cpp") +create_single_source_cgal_program("tangential_relaxation_example.cpp") create_single_source_cgal_program("surface_mesh_intersection.cpp") create_single_source_cgal_program("corefinement_SM.cpp") create_single_source_cgal_program("corefinement_consecutive_bool_op.cpp") diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp new file mode 100644 index 00000000000..0b3fe6646e9 --- /dev/null +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp @@ -0,0 +1,36 @@ +#include +#include +#include +#include + +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel K; +typedef CGAL::Surface_mesh Mesh; + + +namespace PMP = CGAL::Polygon_mesh_processing; + + +int main(int argc, char* argv[]) +{ + const char* filename = (argc > 1) ? argv[1] : "data/elephant.off"; + + Mesh mesh; + if(!PMP::IO::read_polygon_mesh(filename, mesh) || !CGAL::is_triangle_mesh(mesh)) + { + std::cerr << "Invalid input." << std::endl; + return 1; + } + + double target_edge_length = (argc > 2) ? std::stod(std::string(argv[2])) : 0.04; + unsigned int nb_iter = 3; + + std::cout << "Relax..."; + + PMP::tangential_relaxation(mesh); + + std::cout << "done." << std::endl; + + return 0; +} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index 4b0c53e23da..740b2db2827 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -272,7 +272,7 @@ void tangential_relaxation(TriangleMesh& tm, const NamedParameters& np) template void tangential_relaxation(TriangleMesh& tm) { - triangle(tm, parameters::all_default()); + tangential_relaxation(tm, parameters::all_default()); } } } // CGAL::Polygon_mesh_processing From 5f8bfd57954c50b74dcd5c0dd6cad5118ac15bc3 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 14 Sep 2021 11:55:09 +0200 Subject: [PATCH 03/46] fix check_normals(v) to have normals check per surface patch separate surface patches are defined by edge_is_constrained_map and face_patch_map (which complement each other) --- .../connected_components.h | 145 ++++++++++++++++++ .../Isotropic_remeshing/remesh_impl.h | 143 ----------------- .../tangential_relaxation.h | 77 +++++++--- 3 files changed, 204 insertions(+), 161 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h index 670a4c183c9..56f4d3904b0 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -92,6 +93,150 @@ namespace internal { EdgeConstraintMap ecm; }; + + // A property map + template + struct Border_constraint_pmap + { + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + typedef FaceIndexMap FIMap; + + std::shared_ptr< std::set > border_edges_ptr; + const PM* pmesh_ptr_; + + public: + typedef edge_descriptor key_type; + typedef bool value_type; + typedef value_type& reference; + typedef boost::read_write_property_map_tag category; + + Border_constraint_pmap() + : border_edges_ptr(new std::set()) + , pmesh_ptr_(nullptr) + {} + + template + Border_constraint_pmap(const PM& pmesh + , const FaceRange& faces + , const FIMap& fimap) + : border_edges_ptr(new std::set()) + , pmesh_ptr_(&pmesh) + { + std::vector border; + PMP::border_halfedges(faces, *pmesh_ptr_, std::back_inserter(border) + , PMP::parameters::face_index_map(fimap)); + + for (halfedge_descriptor h : border) + border_edges_ptr->insert(edge(h, *pmesh_ptr_)); + } + + friend bool get(const Border_constraint_pmap& map, + const edge_descriptor& e) + { + CGAL_assertion(map.pmesh_ptr_ != nullptr); + return map.border_edges_ptr->count(e) != 0; + } + + friend void put(Border_constraint_pmap& map, + const edge_descriptor& e, + const bool is) + { + CGAL_assertion(map.pmesh_ptr_ != nullptr); + if (is) + map.border_edges_ptr->insert(e); + else + map.border_edges_ptr->erase(e); + } + }; + + template + struct Connected_components_pmap + { + typedef typename boost::graph_traits::face_descriptor face_descriptor; + typedef std::size_t Patch_id; + typedef FaceIndexMap FIMap; + typedef Connected_components_pmap CCMap; + + typedef CGAL::dynamic_face_property_t Face_property_tag; + typedef typename boost::property_map::type Patch_ids_map; + Patch_ids_map patch_ids_map; + std::size_t nb_cc; + + template + bool same_range(const Range& r1, const Range& r2) + { + return boost::begin(r1) == boost::begin(r2) && + boost::end(r1) == boost::end(r2); + } + + template + bool same_range(const Range1& r1, const Range2& r2) + { + return std::distance(boost::begin(r1), boost::end(r1)) == + std::distance(boost::begin(r2), boost::end(r2)); + } + + public: + typedef face_descriptor key_type; + typedef Patch_id value_type; + typedef Patch_id& reference; + typedef boost::read_write_property_map_tag category; + + //note pmesh is a non-const ref because properties are added and removed + //modify the mesh data structure, but not the mesh itself + template + Connected_components_pmap(const FaceRange& face_range + , PM& pmesh + , EdgeIsConstrainedMap ecmap + , FIMap fimap + , const bool do_init = true) + { + patch_ids_map = get(Face_property_tag(), pmesh); + if (do_init) + { +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << "Compute connected components property map." << std::endl; +#endif + if (same_range(face_range, (faces(pmesh)))) + { + // applied on the whole mesh + nb_cc + = PMP::connected_components(pmesh, + patch_ids_map, + PMP::parameters::edge_is_constrained_map(ecmap) + .face_index_map(fimap)); + } + else + { + // applied on a subset of the mesh + nb_cc + = PMP::connected_components(pmesh, + patch_ids_map, + PMP::parameters::edge_is_constrained_map( + make_OR_property_map(ecmap + , internal::Border_constraint_pmap(pmesh, face_range, fimap))) + .face_index_map(fimap)); + } + } + else + nb_cc = 0; // default value + } + + + friend value_type get(const CCMap& m, const key_type& f) + { + if (m.nb_cc == 1) + return 0; + return get(m.patch_ids_map, f); + } + friend void put(CCMap& m, const key_type& f, const value_type i) + { + if (m.nb_cc != 1) + put(m.patch_ids_map, f, i); + } + }; + } // namespace internal /*! diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index f3098e2422a..f8b8c264425 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -84,150 +84,7 @@ namespace internal { ISOLATED_CONSTRAINT //h is constrained, and incident to faces that do not belong to a patch }; - // A property map - template - struct Border_constraint_pmap - { - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - typedef typename boost::graph_traits::edge_descriptor edge_descriptor; - typedef FaceIndexMap FIMap; - std::shared_ptr< std::set > border_edges_ptr; - const PM* pmesh_ptr_; - - public: - typedef edge_descriptor key_type; - typedef bool value_type; - typedef value_type& reference; - typedef boost::read_write_property_map_tag category; - - Border_constraint_pmap() - : border_edges_ptr(new std::set() ) - , pmesh_ptr_(nullptr) - {} - - template - Border_constraint_pmap(const PM& pmesh - , const FaceRange& faces - , const FIMap& fimap) - : border_edges_ptr(new std::set() ) - , pmesh_ptr_(&pmesh) - { - std::vector border; - PMP::border_halfedges(faces, *pmesh_ptr_, std::back_inserter(border) - , PMP::parameters::face_index_map(fimap)); - - for(halfedge_descriptor h : border) - border_edges_ptr->insert(edge(h, *pmesh_ptr_)); - } - - friend bool get(const Border_constraint_pmap& map, - const edge_descriptor& e) - { - CGAL_assertion(map.pmesh_ptr_!=nullptr); - return map.border_edges_ptr->count(e)!=0; - } - - friend void put(Border_constraint_pmap& map, - const edge_descriptor& e, - const bool is) - { - CGAL_assertion(map.pmesh_ptr_ != nullptr); - if (is) - map.border_edges_ptr->insert(e); - else - map.border_edges_ptr->erase(e); - } - }; - - - template - struct Connected_components_pmap - { - typedef typename boost::graph_traits::face_descriptor face_descriptor; - typedef std::size_t Patch_id; - typedef FaceIndexMap FIMap; - typedef Connected_components_pmap CCMap; - - typedef CGAL::dynamic_face_property_t Face_property_tag; - typedef typename boost::property_map::type Patch_ids_map; - Patch_ids_map patch_ids_map; - std::size_t nb_cc; - - template - bool same_range(const Range& r1, const Range& r2) - { - return boost::begin(r1)==boost::begin(r2) && - boost::end(r1)==boost::end(r2); - } - - template - bool same_range(const Range1& r1, const Range2& r2) - { - return std::distance(boost::begin(r1), boost::end(r1)) == - std::distance(boost::begin(r2), boost::end(r2)); - } - - public: - typedef face_descriptor key_type; - typedef Patch_id value_type; - typedef Patch_id& reference; - typedef boost::read_write_property_map_tag category; - - //note pmesh is a non-const ref because properties are added and removed - //modify the mesh data structure, but not the mesh itself - template - Connected_components_pmap(const FaceRange& face_range - , PM& pmesh - , EdgeIsConstrainedMap ecmap - , FIMap fimap - , const bool do_init = true) - { - patch_ids_map = get(Face_property_tag(),pmesh); - if (do_init) - { -#ifdef CGAL_PMP_REMESHING_VERBOSE - std::cout << "Compute connected components property map." << std::endl; -#endif - if ( same_range(face_range, (faces(pmesh))) ) - { - // applied on the whole mesh - nb_cc - = PMP::connected_components(pmesh, - patch_ids_map, - PMP::parameters::edge_is_constrained_map(ecmap) - .face_index_map(fimap)); - } - else - { - // applied on a subset of the mesh - nb_cc - = PMP::connected_components(pmesh, - patch_ids_map, - PMP::parameters::edge_is_constrained_map( - make_OR_property_map(ecmap - , internal::Border_constraint_pmap(pmesh, face_range, fimap) ) ) - .face_index_map(fimap)); - } - } - else - nb_cc=0; // default value - } - - - friend value_type get(const CCMap& m, const key_type& f) - { - if (m.nb_cc == 1) - return 0; - return get(m.patch_ids_map, f); - } - friend void put(CCMap& m, const key_type& f, const value_type i) - { - if (m.nb_cc != 1) - put(m.patch_ids_map, f, i); - } - }; template #include +#include #include #include @@ -68,6 +69,13 @@ namespace Polygon_mesh_processing { * \cgalParamDefault{`1`} * \cgalParamNEnd * +* \cgalParamNBegin{face_index_map} +* \cgalParamDescription{a property map associating to each face of `tm` a unique index between `0` and `num_faces(tm) - 1`} +* \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%face_descriptor` +* as key type and `std::size_t` as value type} +* \cgalParamDefault{an automatically indexed internal map} +* \cgalParamNEnd +* * \cgalParamNBegin{edge_is_constrained_map} * \cgalParamDescription{a property map containing the constrained-or-not status of each edge of `tm`} * \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%edge_descriptor` @@ -94,6 +102,16 @@ namespace Polygon_mesh_processing { * \cgalParamDefault{`false`} * \cgalParamNEnd * +* \cgalParamNBegin{face_patch_map} +* \cgalParamDescription{a property map with the patch id's associated to the faces of `faces(tm)`} +* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%face_descriptor` +* as key type and the desired property, model of `CopyConstructible` and `LessThanComparable` as value type.} +* \cgalParamDefault{a default property map where each face is associated with the ID of +* the connected component it belongs to. Connected components are +* computed with respect to the constrained edges listed in the property map +* `edge_is_constrained_map`} +* \cgalParamExtra{The map is updated during the remeshing process while new faces are created.} +* \cgalParamNEnd * \cgalNamedParamsEnd * * \todo shall we take a range of vertices as input? @@ -135,39 +153,60 @@ void tangential_relaxation(TriangleMesh& tm, const NamedParameters& np) const bool relax_constraints = choose_parameter(get_parameter(np, internal_np::relax_constraints), false); const unsigned int nb_iterations = choose_parameter(get_parameter(np, internal_np::number_of_iterations), 1); + typedef typename GetInitializedFaceIndexMap::type FIMap; + FIMap fimap = CGAL::get_initialized_face_index_map(tm, np); + + typedef typename internal_np::Lookup_named_param_def < + internal_np::face_patch_t, + NamedParameters, + internal::Connected_components_pmap//default + > ::type FPMap; + FPMap fpmap = choose_parameter(get_parameter(np, internal_np::face_patch), + internal::Connected_components_pmap(faces(tm), tm, ecm, fimap, + parameters::is_default_parameter(get_parameter(np, internal_np::face_patch)))); + typedef typename GT::Vector_3 Vector_3; typedef typename GT::Point_3 Point_3; auto check_normals = [&](vertex_descriptor v) { - bool first_run = true; - Vector_3 prev = NULL_VECTOR, first=NULL_VECTOR; + bool first_on_patch = true; + Vector_3 prev = NULL_VECTOR, first = NULL_VECTOR; halfedge_descriptor first_h = boost::graph_traits::null_halfedge(); - for(halfedge_descriptor hd : CGAL::halfedges_around_target(v, tm)) - { - if (is_border(hd, tm)) continue; + for (halfedge_descriptor hd : CGAL::halfedges_around_target(v, tm)) + { + if (is_border(hd, tm) + || get(fpmap, face(hd, tm)) != get(fpmap, face(opposite(hd, tm), tm)) + || get(ecm, edge(hd, tm))) + { + first_on_patch = true; + continue; + } Vector_3 n = compute_face_normal(face(hd, tm), tm); if (n == CGAL::NULL_VECTOR) //for degenerate faces continue; - if (first_run) + if (first_on_patch) { - first_run = false; - first=n; + first_on_patch = false; + first = n; first_h = hd; } else { - if (!get(ecm, edge(hd, tm))) - if( to_double(n * prev) <= 0 ) - return false; - } - prev=n; - } - if (!get(ecm, edge(first_h, tm))) - if( to_double(first * prev) <= 0 ) + if (to_double(n * prev) <= 0) return false; + } + prev = n; + } + + if (!get(ecm, edge(first_h, tm)) + && get(fpmap, face(first_h, tm)) == get(fpmap, face(opposite(first_h, tm), tm))) + { + if (to_double(first * prev) <= 0) + return false; + } return true; }; @@ -188,7 +227,9 @@ void tangential_relaxation(TriangleMesh& tm, const NamedParameters& np) // collect hedges to detect if we have to handle boundary cases for(halfedge_descriptor h : halfedges_around_target(v, tm)) { - if (is_border_edge(h, tm) || get(ecm, edge(h, tm))) + if (is_border_edge(h, tm) + || get(ecm, edge(h, tm)) + || get(fpmap, face(h, tm)) != get(fpmap, face(opposite(h, tm), tm))) border_halfedges.push_back(h); else interior_hedges.push_back(h); @@ -218,7 +259,7 @@ void tangential_relaxation(TriangleMesh& tm, const NamedParameters& np) if (!relax_constraints) continue; Vector_3 vn(NULL_VECTOR); - if (border_halfedges.size() == 2)// corner are constrained + if (border_halfedges.size() == 2)// corners are constrained { vertex_descriptor ph0 = source(border_halfedges[0], tm); vertex_descriptor ph1 = source(border_halfedges[1], tm); From 203de706a998c19778cef9b7d026991e9b93557b Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 14 Sep 2021 12:17:46 +0200 Subject: [PATCH 04/46] rename verbosity macro --- .../internal/Isotropic_remeshing/remesh_impl.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index f8b8c264425..234d314c64d 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -66,6 +66,7 @@ #ifdef CGAL_PMP_REMESHING_VERBOSE_PROGRESS #define CGAL_PMP_REMESHING_VERBOSE +#define CGAL_PMP_TANGENTIAL_RELAXATION_VERBOSE_PROGRESS #endif @@ -857,7 +858,7 @@ namespace internal { #endif for (unsigned int nit = 0; nit < nb_iterations; ++nit) { -#ifdef CGAL_PMP_REMESHING_VERBOSE_PROGRESS +#ifdef CGAL_PMP_TANGENTIAL_RELAXATION_VERBOSE_PROGRESS std::cout << "\r\t(iteration " << (nit + 1) << " / "; std::cout << nb_iterations << ") "; std::cout.flush(); From ff2e1adafd1f6e3bbdc90f9b702c0f775b5d1161 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 14 Sep 2021 12:34:43 +0200 Subject: [PATCH 05/46] add a version of tangential_relaxation that takes a vertex range as input --- .../tangential_relaxation.h | 47 +++++++++++++++---- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index 947970a9f85..d844e829e48 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -26,6 +26,12 @@ #include #include +#ifdef DOXYGEN_RUNNING +#define CGAL_PMP_NP_TEMPLATE_PARAMETERS NamedParameters +#define CGAL_PMP_NP_CLASS NamedParameters +#endif + + namespace CGAL { namespace Polygon_mesh_processing { @@ -36,13 +42,17 @@ namespace Polygon_mesh_processing { * The vertex movement has to be constrained to the vertex tangent plane [...] * smoothing algorithm with uniform Laplacian weights * -* @tparam Triangle model of `MutableFaceGraph`. -* The descriptor types `boost::graph_traits::%face_descriptor` -* and `boost::graph_traits::%halfedge_descriptor` must be +* @tparam TriangleMesh model of `MutableFaceGraph`. +* The descriptor types `boost::graph_traits::%face_descriptor` +* and `boost::graph_traits::%halfedge_descriptor` must be * models of `Hashable`. +* @tparam VertexRange range of `boost::graph_traits::%face_descriptor`, +* model of `Range`. Its iterator type is ForwardIterator. +* * @tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" * -* @param tm a triangle mesh +* @param vertices the range of vertices which will be relocated by relaxation +* @param tm the triangle mesh to which `vertices` belong * @param np an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below * * \cgalNamedParamsBegin @@ -114,11 +124,13 @@ namespace Polygon_mesh_processing { * \cgalParamNEnd * \cgalNamedParamsEnd * -* \todo shall we take a range of vertices as input? +* \todo check if it should really be a triangle mesh or if a polygon mesh is fine */ -template -void tangential_relaxation(TriangleMesh& tm, const NamedParameters& np) -{ + template + void tangential_relaxation(const VertexRange& vertices, + TriangleMesh& tm, + 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; @@ -217,7 +229,7 @@ void tangential_relaxation(TriangleMesh& tm, const NamedParameters& np) std::vector< VNP > barycenters; // at each vertex, compute vertex normal // at each vertex, compute barycenter of neighbors - for(vertex_descriptor v : vertices(tm)) + for(vertex_descriptor v : vertices) { if (get(vcm, v) || halfedge(v, tm)==boost::graph_traits::null_halfedge()) continue; @@ -310,12 +322,27 @@ void tangential_relaxation(TriangleMesh& tm, const NamedParameters& np) }//end for loop (nit == nb_iterations) } +template +void tangential_relaxation(const VertexRange& vertices, TriangleMesh& tm) +{ + tangential_relaxation(vertices, tm, parameters::all_default()); +} + +template +void tangential_relaxation(TriangleMesh& tm, const CGAL_PMP_NP_CLASS& np) +{ + tangential_relaxation(vertices(tm), tm, np); +} + template void tangential_relaxation(TriangleMesh& tm) { - tangential_relaxation(tm, parameters::all_default()); + tangential_relaxation(vertices(tm), tm, parameters::all_default()); } + + } } // CGAL::Polygon_mesh_processing #endif //CGAL_POLYGON_MESH_PROCESSING_TANGENTIAL_RELAXATION_H From 32981602e3dc438f218301f1b096e84c22b33a64 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 14 Sep 2021 12:38:34 +0200 Subject: [PATCH 06/46] add verbosity --- .../internal/Isotropic_remeshing/remesh_impl.h | 6 +++--- .../Polygon_mesh_processing/tangential_relaxation.h | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index 234d314c64d..ef5a22a7f64 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -66,7 +66,7 @@ #ifdef CGAL_PMP_REMESHING_VERBOSE_PROGRESS #define CGAL_PMP_REMESHING_VERBOSE -#define CGAL_PMP_TANGENTIAL_RELAXATION_VERBOSE_PROGRESS +#define CGAL_PMP_TANGENTIAL_RELAXATION_VERBOSE #endif @@ -858,8 +858,8 @@ namespace internal { #endif for (unsigned int nit = 0; nit < nb_iterations; ++nit) { -#ifdef CGAL_PMP_TANGENTIAL_RELAXATION_VERBOSE_PROGRESS - std::cout << "\r\t(iteration " << (nit + 1) << " / "; +#ifdef CGAL_PMP_TANGENTIAL_RELAXATION_VERBOSE + std::cout << "\r\t(Tangential relaxation iteration " << (nit + 1) << " / "; std::cout << nb_iterations << ") "; std::cout.flush(); #endif diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index d844e829e48..a079aedca55 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -225,6 +225,12 @@ namespace Polygon_mesh_processing { for (unsigned int nit = 0; nit < nb_iterations; ++nit) { +#ifdef CGAL_PMP_TANGENTIAL_RELAXATION_VERBOSE + std::cout << "\r\t(Tangential relaxation iteration " << (nit + 1) << " / "; + std::cout << nb_iterations << ") "; + std::cout.flush(); +#endif + typedef std::tuple VNP; std::vector< VNP > barycenters; // at each vertex, compute vertex normal @@ -320,6 +326,11 @@ namespace Polygon_mesh_processing { put(vpm, vp.first, initial_pos);//cancel move } }//end for loop (nit == nb_iterations) + +#ifdef CGAL_PMP_TANGENTIAL_RELAXATION_VERBOSE + std::cout << "\rTangential relaxation : " + << nb_iterations << " iterations done." << std::endl; +#endif } template From 30e30bf164f4361947ff690702019859ada8cda3 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 14 Sep 2021 12:59:54 +0200 Subject: [PATCH 07/46] use externalized tangential_relaxation() in Isotropic_remeshing() --- .../Isotropic_remeshing/remesh_impl.h | 107 +++--------------- 1 file changed, 13 insertions(+), 94 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index ef5a22a7f64..5dbcf74119d 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -856,102 +857,20 @@ namespace internal { std::cout << "Tangential relaxation (" << nb_iterations << " iter.)..."; std::cout << std::endl; #endif - for (unsigned int nit = 0; nit < nb_iterations; ++nit) - { -#ifdef CGAL_PMP_TANGENTIAL_RELAXATION_VERBOSE - std::cout << "\r\t(Tangential relaxation iteration " << (nit + 1) << " / "; - std::cout << nb_iterations << ") "; - std::cout.flush(); -#endif - typedef std::tuple VNP; - std::vector< VNP > barycenters; - // at each vertex, compute vertex normal - // at each vertex, compute barycenter of neighbors - for(vertex_descriptor v : vertices(mesh_)) - { - if (is_constrained(v) || is_isolated(v)) - continue; - else if (is_on_patch(v)) - { - Vector_3 vn = PMP::compute_vertex_normal(v, mesh_, - parameters::vertex_point_map(vpmap_) - .geom_traits(gt_)); - Vector_3 move = CGAL::NULL_VECTOR; - unsigned int star_size = 0; - for(halfedge_descriptor h : halfedges_around_target(v, mesh_)) - { - move = move + Vector_3(get(vpmap_, v), get(vpmap_, source(h, mesh_))); - ++star_size; - } - CGAL_assertion(star_size > 0); //isolated vertices have already been discarded - move = (1. / (double)star_size) * move; + PMP::tangential_relaxation(vertices(mesh_), + mesh_, + CGAL::parameters::edge_is_constrained_map(ecmap_) + .vertex_point_map(vpmap_) + .geom_traits(gt_) + .number_of_iterations(nb_iterations) + .face_index_map(fimap_) + .vertex_is_constrained_map(vcmap_) + .relax_constraints(relax_constraints) + .face_patch_map(patch_ids_map_) + ); - barycenters.push_back( VNP(v, vn, get(vpmap_, v) + move) ); - } - else if (relax_constraints - && !protect_constraints_ - && is_on_patch_border(v) - && !is_corner(v)) - { - Vector_3 vn(NULL_VECTOR); - - std::vector border_halfedges; - for(halfedge_descriptor h : halfedges_around_target(v, mesh_)) - { - if (is_on_patch_border(h) || is_on_patch_border(opposite(h, mesh_))) - border_halfedges.push_back(h); - } - if (border_halfedges.size() == 2)//others are corner cases - { - vertex_descriptor ph0 = source(border_halfedges[0], mesh_); - vertex_descriptor ph1 = source(border_halfedges[1], mesh_); - double dot = to_double(Vector_3(get(vpmap_, v), get(vpmap_, ph0)) - * Vector_3(get(vpmap_, v), get(vpmap_, ph1))); - //check squared cosine is < 0.25 (~120 degrees) - if (0.25 < dot / (sqlength(border_halfedges[0]) * sqlength(border_halfedges[0]))) - barycenters.push_back( VNP(v, vn, CGAL::midpoint(midpoint(border_halfedges[0]), - midpoint(border_halfedges[1]))) ); - } - } - } - - // compute moves - typedef std::pair VP_pair; - std::vector< std::pair > new_locations; - new_locations.reserve(barycenters.size()); - for(const VNP& vnp : barycenters) - { - vertex_descriptor v = std::get<0>(vnp); - Point pv = get(vpmap_, v); - const Vector_3& nv = std::get<1>(vnp); - const Point& qv = std::get<2>(vnp); //barycenter at v - - new_locations.push_back( std::make_pair(v, qv + (nv * Vector_3(qv, pv)) * nv) ); - } - - // perform moves - for(const VP_pair& vp : new_locations) - { - const Point initial_pos = get(vpmap_, vp.first); - const Vector_3 move(initial_pos, vp.second); - - put(vpmap_, vp.first, vp.second); - - //check that no inversion happened - double frac = 1.; - while (frac > 0.03 //5 attempts maximum - && !check_normals(vp.first)) //if a face has been inverted - { - frac = 0.5 * frac; - put(vpmap_, vp.first, initial_pos + frac * move);//shorten the move by 2 - } - if (frac <= 0.02) - put(vpmap_, vp.first, initial_pos);//cancel move - } - - CGAL_assertion(!input_mesh_is_valid_ || is_valid_polygon_mesh(mesh_)); - }//end for loop (nit == nb_iterations) + CGAL_assertion(!input_mesh_is_valid_ || is_valid_polygon_mesh(mesh_)); #ifdef CGAL_PMP_REMESHING_DEBUG debug_self_intersections(); From 9621e79d20e914aa0ca70a7452134804a7b10c47 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 14 Sep 2021 13:09:38 +0200 Subject: [PATCH 08/46] change input (elephant is not in the data folder) --- .../tangential_relaxation_example.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp index 0b3fe6646e9..d5ac513e7ed 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp @@ -14,7 +14,7 @@ namespace PMP = CGAL::Polygon_mesh_processing; int main(int argc, char* argv[]) { - const char* filename = (argc > 1) ? argv[1] : "data/elephant.off"; + const char* filename = (argc > 1) ? argv[1] : "data/pig.off"; Mesh mesh; if(!PMP::IO::read_polygon_mesh(filename, mesh) || !CGAL::is_triangle_mesh(mesh)) @@ -28,7 +28,8 @@ int main(int argc, char* argv[]) std::cout << "Relax..."; - PMP::tangential_relaxation(mesh); + PMP::tangential_relaxation(mesh, + CGAL::parameters::number_of_iterations(3)); std::cout << "done." << std::endl; From 77a6e29ee20b065a1c18fda242095ff083ba0b94 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 14 Sep 2021 13:19:04 +0200 Subject: [PATCH 09/46] fix namespace --- .../CGAL/Polygon_mesh_processing/connected_components.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h index 56f4d3904b0..46e53c1549f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h @@ -123,9 +123,12 @@ namespace internal { : border_edges_ptr(new std::set()) , pmesh_ptr_(&pmesh) { + namespace PMP = CGAL::Polygon_mesh_processing; + std::vector border; - PMP::border_halfedges(faces, *pmesh_ptr_, std::back_inserter(border) - , PMP::parameters::face_index_map(fimap)); + PMP::border_halfedges( + faces, *pmesh_ptr_, std::back_inserter(border) + , PMP::parameters::face_index_map(fimap)); for (halfedge_descriptor h : border) border_edges_ptr->insert(edge(h, *pmesh_ptr_)); @@ -192,6 +195,8 @@ namespace internal { , FIMap fimap , const bool do_init = true) { + namespace PMP = CGAL::Polygon_mesh_processing; + patch_ids_map = get(Face_property_tag(), pmesh); if (do_init) { From f227eede9c87b4be6a670136b29c05659e30e87c Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 14 Sep 2021 15:42:00 +0200 Subject: [PATCH 10/46] use check_normals from b26818c0236e5d92994980ca16b36cc9c1889b27 --- .../tangential_relaxation.h | 64 ++++--------------- 1 file changed, 11 insertions(+), 53 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index a079aedca55..adc7000a0e9 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -17,7 +17,6 @@ #include #include -#include #include #include @@ -47,8 +46,7 @@ namespace Polygon_mesh_processing { * and `boost::graph_traits::%halfedge_descriptor` must be * models of `Hashable`. * @tparam VertexRange range of `boost::graph_traits::%face_descriptor`, -* model of `Range`. Its iterator type is ForwardIterator. -* +* model of `Range`. Its iterator type is `ForwardIterator`. * @tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" * * @param vertices the range of vertices which will be relocated by relaxation @@ -79,13 +77,6 @@ namespace Polygon_mesh_processing { * \cgalParamDefault{`1`} * \cgalParamNEnd * -* \cgalParamNBegin{face_index_map} -* \cgalParamDescription{a property map associating to each face of `tm` a unique index between `0` and `num_faces(tm) - 1`} -* \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%face_descriptor` -* as key type and `std::size_t` as value type} -* \cgalParamDefault{an automatically indexed internal map} -* \cgalParamNEnd -* * \cgalParamNBegin{edge_is_constrained_map} * \cgalParamDescription{a property map containing the constrained-or-not status of each edge of `tm`} * \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%edge_descriptor` @@ -112,16 +103,6 @@ namespace Polygon_mesh_processing { * \cgalParamDefault{`false`} * \cgalParamNEnd * -* \cgalParamNBegin{face_patch_map} -* \cgalParamDescription{a property map with the patch id's associated to the faces of `faces(tm)`} -* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%face_descriptor` -* as key type and the desired property, model of `CopyConstructible` and `LessThanComparable` as value type.} -* \cgalParamDefault{a default property map where each face is associated with the ID of -* the connected component it belongs to. Connected components are -* computed with respect to the constrained edges listed in the property map -* `edge_is_constrained_map`} -* \cgalParamExtra{The map is updated during the remeshing process while new faces are created.} -* \cgalParamNEnd * \cgalNamedParamsEnd * * \todo check if it should really be a triangle mesh or if a polygon mesh is fine @@ -165,60 +146,39 @@ namespace Polygon_mesh_processing { const bool relax_constraints = choose_parameter(get_parameter(np, internal_np::relax_constraints), false); const unsigned int nb_iterations = choose_parameter(get_parameter(np, internal_np::number_of_iterations), 1); - typedef typename GetInitializedFaceIndexMap::type FIMap; - FIMap fimap = CGAL::get_initialized_face_index_map(tm, np); - - typedef typename internal_np::Lookup_named_param_def < - internal_np::face_patch_t, - NamedParameters, - internal::Connected_components_pmap//default - > ::type FPMap; - FPMap fpmap = choose_parameter(get_parameter(np, internal_np::face_patch), - internal::Connected_components_pmap(faces(tm), tm, ecm, fimap, - parameters::is_default_parameter(get_parameter(np, internal_np::face_patch)))); - typedef typename GT::Vector_3 Vector_3; typedef typename GT::Point_3 Point_3; auto check_normals = [&](vertex_descriptor v) { - bool first_on_patch = true; + bool first_run = true; Vector_3 prev = NULL_VECTOR, first = NULL_VECTOR; halfedge_descriptor first_h = boost::graph_traits::null_halfedge(); - for (halfedge_descriptor hd : CGAL::halfedges_around_target(v, tm)) { - if (is_border(hd, tm) - || get(fpmap, face(hd, tm)) != get(fpmap, face(opposite(hd, tm), tm)) - || get(ecm, edge(hd, tm))) - { - first_on_patch = true; - continue; - } + if (is_border(hd, tm)) continue; + Vector_3 n = compute_face_normal(face(hd, tm), tm); if (n == CGAL::NULL_VECTOR) //for degenerate faces continue; - if (first_on_patch) + if (first_run) { - first_on_patch = false; + first_run = false; first = n; first_h = hd; } else { - if (to_double(n * prev) <= 0) - return false; + if (!get(ecm, edge(hd, tm))) + if (to_double(n * prev) <= 0) + return false; } prev = n; } - - if (!get(ecm, edge(first_h, tm)) - && get(fpmap, face(first_h, tm)) == get(fpmap, face(opposite(first_h, tm), tm))) - { + if (!get(ecm, edge(first_h, tm))) if (to_double(first * prev) <= 0) return false; - } return true; }; @@ -245,9 +205,7 @@ namespace Polygon_mesh_processing { // collect hedges to detect if we have to handle boundary cases for(halfedge_descriptor h : halfedges_around_target(v, tm)) { - if (is_border_edge(h, tm) - || get(ecm, edge(h, tm)) - || get(fpmap, face(h, tm)) != get(fpmap, face(opposite(h, tm), tm))) + if (is_border_edge(h, tm) || get(ecm, edge(h, tm))) border_halfedges.push_back(h); else interior_hedges.push_back(h); From bb674b571c4395210aff98b3ed7f780729332582 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 14 Sep 2021 15:42:43 +0200 Subject: [PATCH 11/46] use property maps to define "general" constrained edges and vertices before relaxation --- .../Isotropic_remeshing/remesh_impl.h | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index 5dbcf74119d..bdc4d00e9ad 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -858,16 +859,38 @@ namespace internal { std::cout << std::endl; #endif + // property map of constrained edges for relaxation + auto edge_constraint = [&](const edge_descriptor e) + { + return this->is_constrained(e); + }; + auto constrained_edges_pmap + = boost::make_function_property_map(edge_constraint); + + // property map of constrained vertices for relaxation + auto vertex_constraint = [&](const vertex_descriptor v) + { + for (halfedge_descriptor h : halfedges_around_target(v, mesh_)) + { + Halfedge_status s = status(h); + if ( s == PATCH + || s == PATCH_BORDER + || status(opposite(h, mesh_)) == PATCH_BORDER) + return false; + } + return true; + }; + auto constrained_vertices_pmap + = boost::make_function_property_map(vertex_constraint); + PMP::tangential_relaxation(vertices(mesh_), mesh_, - CGAL::parameters::edge_is_constrained_map(ecmap_) + CGAL::parameters::number_of_iterations(nb_iterations) .vertex_point_map(vpmap_) .geom_traits(gt_) - .number_of_iterations(nb_iterations) - .face_index_map(fimap_) - .vertex_is_constrained_map(vcmap_) + .edge_is_constrained_map(constrained_edges_pmap) + .vertex_is_constrained_map(constrained_vertices_pmap) .relax_constraints(relax_constraints) - .face_patch_map(patch_ids_map_) ); CGAL_assertion(!input_mesh_is_valid_ || is_valid_polygon_mesh(mesh_)); From c63729759e723583aed0e706966a976be477a718 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 14 Sep 2021 15:56:10 +0200 Subject: [PATCH 12/46] move code back to remesh_impl.h --- .../connected_components.h | 150 ------------------ .../Isotropic_remeshing/remesh_impl.h | 143 +++++++++++++++++ 2 files changed, 143 insertions(+), 150 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h index 46e53c1549f..670a4c183c9 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/connected_components.h @@ -31,7 +31,6 @@ #include #include #include -#include #include #include @@ -93,155 +92,6 @@ namespace internal { EdgeConstraintMap ecm; }; - - // A property map - template - struct Border_constraint_pmap - { - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - typedef typename boost::graph_traits::edge_descriptor edge_descriptor; - typedef FaceIndexMap FIMap; - - std::shared_ptr< std::set > border_edges_ptr; - const PM* pmesh_ptr_; - - public: - typedef edge_descriptor key_type; - typedef bool value_type; - typedef value_type& reference; - typedef boost::read_write_property_map_tag category; - - Border_constraint_pmap() - : border_edges_ptr(new std::set()) - , pmesh_ptr_(nullptr) - {} - - template - Border_constraint_pmap(const PM& pmesh - , const FaceRange& faces - , const FIMap& fimap) - : border_edges_ptr(new std::set()) - , pmesh_ptr_(&pmesh) - { - namespace PMP = CGAL::Polygon_mesh_processing; - - std::vector border; - PMP::border_halfedges( - faces, *pmesh_ptr_, std::back_inserter(border) - , PMP::parameters::face_index_map(fimap)); - - for (halfedge_descriptor h : border) - border_edges_ptr->insert(edge(h, *pmesh_ptr_)); - } - - friend bool get(const Border_constraint_pmap& map, - const edge_descriptor& e) - { - CGAL_assertion(map.pmesh_ptr_ != nullptr); - return map.border_edges_ptr->count(e) != 0; - } - - friend void put(Border_constraint_pmap& map, - const edge_descriptor& e, - const bool is) - { - CGAL_assertion(map.pmesh_ptr_ != nullptr); - if (is) - map.border_edges_ptr->insert(e); - else - map.border_edges_ptr->erase(e); - } - }; - - template - struct Connected_components_pmap - { - typedef typename boost::graph_traits::face_descriptor face_descriptor; - typedef std::size_t Patch_id; - typedef FaceIndexMap FIMap; - typedef Connected_components_pmap CCMap; - - typedef CGAL::dynamic_face_property_t Face_property_tag; - typedef typename boost::property_map::type Patch_ids_map; - Patch_ids_map patch_ids_map; - std::size_t nb_cc; - - template - bool same_range(const Range& r1, const Range& r2) - { - return boost::begin(r1) == boost::begin(r2) && - boost::end(r1) == boost::end(r2); - } - - template - bool same_range(const Range1& r1, const Range2& r2) - { - return std::distance(boost::begin(r1), boost::end(r1)) == - std::distance(boost::begin(r2), boost::end(r2)); - } - - public: - typedef face_descriptor key_type; - typedef Patch_id value_type; - typedef Patch_id& reference; - typedef boost::read_write_property_map_tag category; - - //note pmesh is a non-const ref because properties are added and removed - //modify the mesh data structure, but not the mesh itself - template - Connected_components_pmap(const FaceRange& face_range - , PM& pmesh - , EdgeIsConstrainedMap ecmap - , FIMap fimap - , const bool do_init = true) - { - namespace PMP = CGAL::Polygon_mesh_processing; - - patch_ids_map = get(Face_property_tag(), pmesh); - if (do_init) - { -#ifdef CGAL_PMP_REMESHING_VERBOSE - std::cout << "Compute connected components property map." << std::endl; -#endif - if (same_range(face_range, (faces(pmesh)))) - { - // applied on the whole mesh - nb_cc - = PMP::connected_components(pmesh, - patch_ids_map, - PMP::parameters::edge_is_constrained_map(ecmap) - .face_index_map(fimap)); - } - else - { - // applied on a subset of the mesh - nb_cc - = PMP::connected_components(pmesh, - patch_ids_map, - PMP::parameters::edge_is_constrained_map( - make_OR_property_map(ecmap - , internal::Border_constraint_pmap(pmesh, face_range, fimap))) - .face_index_map(fimap)); - } - } - else - nb_cc = 0; // default value - } - - - friend value_type get(const CCMap& m, const key_type& f) - { - if (m.nb_cc == 1) - return 0; - return get(m.patch_ids_map, f); - } - friend void put(CCMap& m, const key_type& f, const value_type i) - { - if (m.nb_cc != 1) - put(m.patch_ids_map, f, i); - } - }; - } // namespace internal /*! diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index bdc4d00e9ad..f45383d7555 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -87,7 +87,150 @@ namespace internal { ISOLATED_CONSTRAINT //h is constrained, and incident to faces that do not belong to a patch }; + // A property map + template + struct Border_constraint_pmap + { + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::edge_descriptor edge_descriptor; + typedef FaceIndexMap FIMap; + std::shared_ptr< std::set > border_edges_ptr; + const PM* pmesh_ptr_; + + public: + typedef edge_descriptor key_type; + typedef bool value_type; + typedef value_type& reference; + typedef boost::read_write_property_map_tag category; + + Border_constraint_pmap() + : border_edges_ptr(new std::set() ) + , pmesh_ptr_(nullptr) + {} + + template + Border_constraint_pmap(const PM& pmesh + , const FaceRange& faces + , const FIMap& fimap) + : border_edges_ptr(new std::set() ) + , pmesh_ptr_(&pmesh) + { + std::vector border; + PMP::border_halfedges(faces, *pmesh_ptr_, std::back_inserter(border) + , PMP::parameters::face_index_map(fimap)); + + for(halfedge_descriptor h : border) + border_edges_ptr->insert(edge(h, *pmesh_ptr_)); + } + + friend bool get(const Border_constraint_pmap& map, + const edge_descriptor& e) + { + CGAL_assertion(map.pmesh_ptr_!=nullptr); + return map.border_edges_ptr->count(e)!=0; + } + + friend void put(Border_constraint_pmap& map, + const edge_descriptor& e, + const bool is) + { + CGAL_assertion(map.pmesh_ptr_ != nullptr); + if (is) + map.border_edges_ptr->insert(e); + else + map.border_edges_ptr->erase(e); + } + }; + + + template + struct Connected_components_pmap + { + typedef typename boost::graph_traits::face_descriptor face_descriptor; + typedef std::size_t Patch_id; + typedef FaceIndexMap FIMap; + typedef Connected_components_pmap CCMap; + + typedef CGAL::dynamic_face_property_t Face_property_tag; + typedef typename boost::property_map::type Patch_ids_map; + Patch_ids_map patch_ids_map; + std::size_t nb_cc; + + template + bool same_range(const Range& r1, const Range& r2) + { + return boost::begin(r1)==boost::begin(r2) && + boost::end(r1)==boost::end(r2); + } + + template + bool same_range(const Range1& r1, const Range2& r2) + { + return std::distance(boost::begin(r1), boost::end(r1)) == + std::distance(boost::begin(r2), boost::end(r2)); + } + + public: + typedef face_descriptor key_type; + typedef Patch_id value_type; + typedef Patch_id& reference; + typedef boost::read_write_property_map_tag category; + + //note pmesh is a non-const ref because properties are added and removed + //modify the mesh data structure, but not the mesh itself + template + Connected_components_pmap(const FaceRange& face_range + , PM& pmesh + , EdgeIsConstrainedMap ecmap + , FIMap fimap + , const bool do_init = true) + { + patch_ids_map = get(Face_property_tag(),pmesh); + if (do_init) + { +#ifdef CGAL_PMP_REMESHING_VERBOSE + std::cout << "Compute connected components property map." << std::endl; +#endif + if ( same_range(face_range, (faces(pmesh))) ) + { + // applied on the whole mesh + nb_cc + = PMP::connected_components(pmesh, + patch_ids_map, + PMP::parameters::edge_is_constrained_map(ecmap) + .face_index_map(fimap)); + } + else + { + // applied on a subset of the mesh + nb_cc + = PMP::connected_components(pmesh, + patch_ids_map, + PMP::parameters::edge_is_constrained_map( + make_OR_property_map(ecmap + , internal::Border_constraint_pmap(pmesh, face_range, fimap) ) ) + .face_index_map(fimap)); + } + } + else + nb_cc=0; // default value + } + + + friend value_type get(const CCMap& m, const key_type& f) + { + if (m.nb_cc == 1) + return 0; + return get(m.patch_ids_map, f); + } + friend void put(CCMap& m, const key_type& f, const value_type i) + { + if (m.nb_cc != 1) + put(m.patch_ids_map, f, i); + } + }; template Date: Tue, 14 Sep 2021 16:48:13 +0200 Subject: [PATCH 13/46] rephrase --- .../CGAL/Polygon_mesh_processing/tangential_relaxation.h | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index adc7000a0e9..716cf390ead 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -36,10 +36,11 @@ namespace Polygon_mesh_processing { /*! * \ingroup PMP_meshing_grp -* \todo rephrase -* applies an iterative smoothing filter to the mesh. -* The vertex movement has to be constrained to the vertex tangent plane [...] -* smoothing algorithm with uniform Laplacian weights +* applies an iterative area-based tangential smoothing to the given range of vertices. +* Each vertex is relocated to its gravity-weighted centroid, iteratively, and the connectivity +* is unchanged. +* For each vertex `v`, the relocation vector is projected back to the tangent plane to +* the surface at `v`. * * @tparam TriangleMesh model of `MutableFaceGraph`. * The descriptor types `boost::graph_traits::%face_descriptor` From d6fcc34cc3d5cf14e7dc04d655af041011b4cb34 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 14 Sep 2021 17:06:43 +0200 Subject: [PATCH 14/46] add doc in user manual --- .../doc/Polygon_mesh_processing/Polygon_mesh_processing.txt | 6 ++++++ .../doc/Polygon_mesh_processing/examples.txt | 1 + 2 files changed, 7 insertions(+) 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 8757bba98d4..adf70916e98 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 @@ -165,6 +165,12 @@ Mesh smoothing of the closed surface blobby, containing self-intersection Statistics for the various combinations of mesh smoothing. \cgalFigureEnd +Mesh smoothing can also be achieved with `CGAL::Polygon_mesh_processing::tangential_relaxation()`, +that iteratively performs vertex relocations following an area-based tangential smoothing scheme. +The example \ref Polygon_mesh_processing/tangential_relaxation_example.cpp shows how this +mesh relaxation function can be used. + + - Shape smoothing: `CGAL::Polygon_mesh_processing::smooth_shape()` incrementally moves vertices towards a weighted barycenter of their neighbors along the mean curvature flow. The curvature flow algorithm for shape smoothing is based on Desbrun et al. \cgalCite{cgal:dmsb-ifamdcf-99} and on Kazhdan et al. \cgalCite{kazhdan2012can}. diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt index 496897e2349..f80bacafc1e 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/examples.txt @@ -31,6 +31,7 @@ \example Polygon_mesh_processing/repair_polygon_soup_example.cpp \example Polygon_mesh_processing/mesh_smoothing_example.cpp \example Polygon_mesh_processing/shape_smoothing_example.cpp +\example Polygon_mesh_processing/tangential_relaxation_example.cpp \example Polygon_mesh_processing/locate_example.cpp \example Polygon_mesh_processing/orientation_pipeline_example.cpp \example Polygon_mesh_processing/triangulate_faces_split_visitor_example.cpp From 3b0971dae3d80ad674c8cce1e0f75e1be6b7d474 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 14 Sep 2021 17:09:04 +0200 Subject: [PATCH 15/46] reference manual --- .../Polygon_mesh_processing/PackageDescription.txt | 1 + .../Polygon_mesh_processing/tangential_relaxation.h | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt index e678bf1627f..718c8d9b1f3 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt @@ -102,6 +102,7 @@ The page \ref bgl_namedparameters "Named Parameters" describes their usage. - `CGAL::Polygon_mesh_processing::fair()` - `CGAL::Polygon_mesh_processing::refine()` - `CGAL::Polygon_mesh_processing::smooth_mesh()` +- `CGAL::Polygon_mesh_processing::tangential_relaxation()` - `CGAL::Polygon_mesh_processing::smooth_shape()` - `CGAL::Polygon_mesh_processing::triangulate_face()` - `CGAL::Polygon_mesh_processing::triangulate_faces()` diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index 716cf390ead..90a1bcf1e88 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -37,10 +37,9 @@ namespace Polygon_mesh_processing { /*! * \ingroup PMP_meshing_grp * applies an iterative area-based tangential smoothing to the given range of vertices. -* Each vertex is relocated to its gravity-weighted centroid, iteratively, and the connectivity -* is unchanged. -* For each vertex `v`, the relocation vector is projected back to the tangent plane to -* the surface at `v`. +* Each vertex `v` is relocated to its gravity-weighted centroid, and the relocation vector +* is projected back to the tangent plane to the surface at `v`, iteratively. +* The connectivity remains unchanged. * * @tparam TriangleMesh model of `MutableFaceGraph`. * The descriptor types `boost::graph_traits::%face_descriptor` @@ -298,6 +297,10 @@ void tangential_relaxation(const VertexRange& vertices, TriangleMesh& tm) tangential_relaxation(vertices, tm, parameters::all_default()); } +/*! +* \ingroup PMP_meshing_grp +* applies `tangential_relaxation()` to all the vertices of `tm`. +*/ template void tangential_relaxation(TriangleMesh& tm, const CGAL_PMP_NP_CLASS& np) From 60648b5209743ee9d30b05397f5c61733487f34a Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Fri, 17 Sep 2021 09:48:59 +0200 Subject: [PATCH 16/46] add tangential relaxation to the Smoothing_plugin --- .../Polyhedron/Plugins/PMP/CMakeLists.txt | 2 +- .../Plugins/PMP/Smoothing_plugin.cpp | 106 ++++++++++- .../PMP/Smoothing_tangential_relaxation.ui | 177 ++++++++++++++++++ 3 files changed, 283 insertions(+), 2 deletions(-) create mode 100644 Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_tangential_relaxation.ui diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/CMakeLists.txt b/Polyhedron/demo/Polyhedron/Plugins/PMP/CMakeLists.txt index 26c24ce0875..76b60fbd535 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/CMakeLists.txt +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/CMakeLists.txt @@ -38,7 +38,7 @@ if(TARGET CGAL::Eigen3_support) demo_framework CGAL::Eigen3_support) # The smoothing plugin can still do some things, even if Ceres is not found - qt5_wrap_ui( smoothingUI_FILES Smoothing_plugin.ui) + qt5_wrap_ui( smoothingUI_FILES Smoothing_plugin.ui Smoothing_tangential_relaxation.ui) polyhedron_demo_plugin(smoothing_plugin Smoothing_plugin ${smoothingUI_FILES}) target_link_libraries(smoothing_plugin PUBLIC scene_surface_mesh_item scene_selection_item CGAL::Eigen3_support) find_package(Ceres QUIET) diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp index 15e41fe939b..060711365df 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp @@ -10,12 +10,14 @@ #include #include +#include #include "Scene.h" #include "Scene_surface_mesh_item.h" #include "Scene_polyhedron_selection_item.h" #include "ui_Smoothing_plugin.h" +#include "ui_Smoothing_tangential_relaxation.h" #include #include @@ -69,11 +71,15 @@ public: connect(ui_widget.mesh_smoothing_button, SIGNAL(clicked()), this, SLOT(on_mesh_smoothing_clicked())); connect(ui_widget.shape_smoothing_button, SIGNAL(clicked()), this, SLOT(on_shape_smoothing_clicked())); + + actionRelax_ = new QAction(tr("Tangential Relaxation"), mw); + actionRelax_->setProperty("subMenuName", "Polygon Mesh Processing"); + connect(actionRelax_, SIGNAL(triggered()), this, SLOT(tangential_relaxation_action())); } QList actions() const { - return QList() << actionSmoothing_; + return QList() << actionSmoothing_ << actionRelax_; } bool applicable(QAction*) const @@ -127,6 +133,24 @@ public: } } + Ui::Tangential_relaxation_dialog + relaxation_dialog(QDialog* dialog, + Scene_facegraph_item* poly_item) + { + Ui::Tangential_relaxation_dialog ui; + ui.setupUi(dialog); + connect(ui.buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); + connect(ui.buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); + + ui.nbIterations_spinbox->setSingleStep(1); + ui.nbIterations_spinbox->setRange(1/*min*/, 1000/*max*/); + ui.nbIterations_spinbox->setValue(1); + + ui.smooth1D_checkbox->setChecked(true); + + return ui; + } + public Q_SLOTS: void smoothing_action() { @@ -145,6 +169,85 @@ public Q_SLOTS: } } + void tangential_relaxation_action() + { + const Scene_interface::Item_id index = scene->mainSelectionIndex(); + + Scene_facegraph_item* poly_item = + qobject_cast(scene->item(index)); + Scene_polyhedron_selection_item* selection_item = + qobject_cast(scene->item(index)); + + if (poly_item || selection_item) + { + if (selection_item && selection_item->selected_facets.empty()) + { + QMessageBox::warning(mw, "Empty Facets", "There are no selected facets. Aborting."); + return; + } + // Create dialog box + QDialog dialog(mw); + Ui::Tangential_relaxation_dialog ui = relaxation_dialog(&dialog, poly_item); + + // Get values + int i = dialog.exec(); + if (i == QDialog::Rejected) + { + std::cout << "Tangential relaxation aborted" << std::endl; + return; + } + + unsigned int nb_iter = ui.nbIterations_spinbox->value(); + bool smooth_features = ui.smooth1D_checkbox->isChecked(); + + // wait cursor + QApplication::setOverrideCursor(Qt::WaitCursor); + QElapsedTimer time; + time.start(); + + FaceGraph& pmesh = (poly_item != nullptr) + ? *poly_item->polyhedron() + : *selection_item->polyhedron(); + + if (selection_item) + { + boost::unordered_set vset; + for (face_descriptor f : selection_item->selected_facets) + { + for(vertex_descriptor fv : CGAL::vertices_around_face(halfedge(f, pmesh), pmesh)) + vset.insert(fv); + } + + CGAL::Polygon_mesh_processing::tangential_relaxation( + vset, + pmesh, + CGAL::Polygon_mesh_processing::parameters::number_of_iterations(nb_iter) + .edge_is_constrained_map(selection_item->constrained_edges_pmap()) + .vertex_is_constrained_map(selection_item->constrained_vertices_pmap())); + + selection_item->polyhedron_item()->invalidateOpenGLBuffers(); + Q_EMIT selection_item->polyhedron_item()->itemChanged(); + selection_item->invalidateOpenGLBuffers(); + selection_item->setKeepSelectionValid(Scene_polyhedron_selection_item::None); + } + else if (poly_item) + { + CGAL::Polygon_mesh_processing::tangential_relaxation( + vertices(pmesh), + pmesh, + CGAL::Polygon_mesh_processing::parameters::number_of_iterations(nb_iter)); + + poly_item->invalidateOpenGLBuffers(); + Q_EMIT poly_item->itemChanged(); + } + + std::cout << "ok (" << time.elapsed() << " ms)" << std::endl; + } + + // default cursor + QApplication::restoreOverrideCursor(); + } + void on_mesh_smoothing_clicked() { const Scene_interface::Item_id index = scene->mainSelectionIndex(); @@ -284,6 +387,7 @@ public Q_SLOTS: private: QAction* actionSmoothing_; + QAction* actionRelax_; QDockWidget* dock_widget; Ui::Smoothing ui_widget; }; diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_tangential_relaxation.ui b/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_tangential_relaxation.ui new file mode 100644 index 00000000000..fd9c4aefe1d --- /dev/null +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_tangential_relaxation.ui @@ -0,0 +1,177 @@ + + + Tangential_relaxation_dialog + + + true + + + + 0 + 0 + 376 + 206 + + + + Isotropic remeshing criteria + + + + + + + 15 + 75 + true + + + + NO OBJECT + + + + + + + No size + + + + + + + Tangential Relaxation + + + + + + Number of iterations + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + nbIterations_spinbox + + + + + + + + 110 + 0 + + + + 1 + + + + + + + Allow 1D relaxation + (along borders/constraints) + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Qt::Vertical + + + QSizePolicy::Maximum + + + + 20 + 40 + + + + + + + + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::MinimumExpanding + + + + 0 + 0 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + nbIterations_spinbox + smooth1D_checkbox + + + + + buttonBox + accepted() + Tangential_relaxation_dialog + accept() + + + 397 + 333 + + + 157 + 195 + + + + + buttonBox + rejected() + Tangential_relaxation_dialog + reject() + + + 397 + 333 + + + 286 + 195 + + + + + From 9963e1e368a2a0d9450ab08f49512cc06c01b5c5 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Fri, 17 Sep 2021 10:05:35 +0200 Subject: [PATCH 17/46] user manual --- .../doc/Polygon_mesh_processing/Polygon_mesh_processing.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 adf70916e98..92b442267db 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 @@ -165,8 +165,9 @@ Mesh smoothing of the closed surface blobby, containing self-intersection Statistics for the various combinations of mesh smoothing. \cgalFigureEnd -Mesh smoothing can also be achieved with `CGAL::Polygon_mesh_processing::tangential_relaxation()`, -that iteratively performs vertex relocations following an area-based tangential smoothing scheme. +- Mesh smoothing by tangential relaxation : `CGAL::Polygon_mesh_processing::tangential_relaxation()` +iteratively performs vertex relocations following an area-based tangential smoothing scheme described in +\cgalCite{botsch2010PMP}. The example \ref Polygon_mesh_processing/tangential_relaxation_example.cpp shows how this mesh relaxation function can be used. From 752b5885aaa136c3eac4a4da53787c36ff977f8a Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Fri, 17 Sep 2021 14:43:01 +0200 Subject: [PATCH 18/46] add a test --- .../test_mesh_smoothing.cpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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 6903eb0312b..fa1d90d01e1 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 @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -82,6 +83,18 @@ void test_area_smoothing_without_projection(const char* filename) .use_angle_smoothing(false)); } +template +void test_tangential_relaxation(const char* filename) +{ + Mesh mesh; + read_mesh(filename, mesh); + PMP::tangential_relaxation(vertices(mesh), mesh, + CGAL::parameters::number_of_iterations(4) + .relax_constraints(false)); + PMP::tangential_relaxation(vertices(mesh), mesh); + PMP::tangential_relaxation(mesh); +} + template void test_constrained_vertices(const char* filename) { @@ -127,6 +140,7 @@ int main(int /*argc*/, char** /*argv*/) test_angle_smoothing_without_projection(filename_elephant); test_area_smoothing_without_projection(filename_mannequin); test_constrained_vertices(filename_elephant); + test_tangential_relaxation(filename_elephant); // test with Polyhedron test_smoothing(filename_elephant); @@ -135,6 +149,7 @@ int main(int /*argc*/, char** /*argv*/) test_angle_smoothing_without_projection(filename_elephant); test_area_smoothing_without_projection(filename_mannequin); test_constrained_vertices(filename_mannequin); + test_tangential_relaxation(filename_elephant); std::cout << "Done!" << std::endl; return EXIT_SUCCESS; From 6357211695c020f324970b030efda78cf175a57d Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Mon, 27 Sep 2021 17:05:14 +0200 Subject: [PATCH 19/46] deprecate smooth_mesh() and rename it angle_and_area_smoothing() --- .../PackageDescription.txt | 6 +- .../mesh_smoothing_example.cpp | 4 +- .../angle_and_area_smoothing.h | 378 ++++++++++++++++++ .../Polygon_mesh_processing/smooth_mesh.h | 334 +--------------- .../test_mesh_smoothing.cpp | 20 +- 5 files changed, 410 insertions(+), 332 deletions(-) create mode 100644 Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/angle_and_area_smoothing.h diff --git a/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt b/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt index 718c8d9b1f3..51f46988988 100644 --- a/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt +++ b/Polygon_mesh_processing/doc/Polygon_mesh_processing/PackageDescription.txt @@ -65,6 +65,9 @@ /// \defgroup PMP_IO_grp I/O Functions /// \ingroup PkgPolygonMeshProcessingRef +/// \defgroup PMPDeprecated Deprecated Functions +/// \ingroup PkgPolygonMeshProcessingRef + /*! \addtogroup PkgPolygonMeshProcessingRef @@ -101,7 +104,8 @@ The page \ref bgl_namedparameters "Named Parameters" describes their usage. \cgalCRPSection{Meshing Functions} - `CGAL::Polygon_mesh_processing::fair()` - `CGAL::Polygon_mesh_processing::refine()` -- `CGAL::Polygon_mesh_processing::smooth_mesh()` +- `CGAL::Polygon_mesh_processing::smooth_mesh()` (deprecated) +- `CGAL::Polygon_mesh_processing::angle_and_area_smoothing()` - `CGAL::Polygon_mesh_processing::tangential_relaxation()` - `CGAL::Polygon_mesh_processing::smooth_shape()` - `CGAL::Polygon_mesh_processing::triangulate_face()` 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 9aff9d2306d..e78befe50da 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,7 +1,7 @@ #include #include -#include +#include #include #include @@ -43,7 +43,7 @@ int main(int argc, char** argv) std::cout << "Smoothing mesh... (" << nb_iterations << " iterations)" << std::endl; // Smooth with both angle and area criteria + Delaunay flips - PMP::smooth_mesh(mesh, PMP::parameters::number_of_iterations(nb_iterations) + PMP::angle_and_area_smoothing(mesh, PMP::parameters::number_of_iterations(nb_iterations) .use_safety_constraints(false) // authorize all moves .edge_is_constrained_map(eif)); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/angle_and_area_smoothing.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/angle_and_area_smoothing.h new file mode 100644 index 00000000000..a20437ba1c8 --- /dev/null +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/angle_and_area_smoothing.h @@ -0,0 +1,378 @@ +// Copyright (c) 2018 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Mael Rouxel-Labbé +// Konstantinos Katrioplas (konst.katrioplas@gmail.com) + +#ifndef CGAL_POLYGON_MESH_PROCESSING_ANGLE_AND_AREA_SMOOTHING_H +#define CGAL_POLYGON_MESH_PROCESSING_ANGLE_AND_AREA_SMOOTHING_H + +#include + +#include +#include +#include + +#include +#include + +#include + +#ifdef DOXYGEN_RUNNING +#define CGAL_PMP_NP_TEMPLATE_PARAMETERS NamedParameters +#define CGAL_PMP_NP_CLASS NamedParameters +#endif + +namespace CGAL { +namespace Polygon_mesh_processing { + +/*! +* \ingroup PMP_meshing_grp +* +* \short smooths a triangulated region of a polygon mesh. +* +* This function attempts to make the triangle angle and area distributions as uniform as possible +* by moving (non-constrained) vertices. +* +* 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. +* +* Optionally, the points are reprojected 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 bgl_namedparameters "Named Parameters" +* +* @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 an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below +* +* \cgalNamedParamsBegin +* \cgalParamNBegin{number_of_iterations} +* \cgalParamDescription{the number of iterations for the sequence of the smoothing iterations performed} +* \cgalParamType{unsigned int} +* \cgalParamDefault{`1`} +* \cgalParamNEnd +* +* \cgalParamNBegin{use_angle_smoothing} +* \cgalParamDescription{value to indicate whether angle-based smoothing should be used} +* \cgalParamType{Boolean} +* \cgalParamDefault{`true`} +* \cgalParamNEnd +* +* \cgalParamNBegin{use_area_smoothing} +* \cgalParamDescription{value to indicate whether area-based smoothing should be used} +* \cgalParamType{Boolean} +* \cgalParamDefault{`true`} +* \cgalParamNEnd +* +* \cgalParamNBegin{vertex_point_map} +* \cgalParamDescription{a property map associating points to the vertices of `tmesh`} +* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%vertex_descriptor` +* as key type and `%Point_3` as value type} +* \cgalParamDefault{`boost::get(CGAL::vertex_point, tmesh)`} +* \cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t` +* must be available in `TriangleMesh`.} +* \cgalParamNEnd +* +* \cgalParamNBegin{geom_traits} +* \cgalParamDescription{an instance of a geometric traits class} +* \cgalParamType{a class model of `Kernel`} +* \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`} +* \cgalParamExtra{The geometric traits class must be compatible with the vertex point type.} +* \cgalParamNEnd +* +* \cgalParamNBegin{use_safety_constraints} +* \cgalParamDescription{If `true`, vertex moves that would worsen the mesh are ignored.} +* \cgalParamType{Boolean} +* \cgalParamDefault{`false`} +* \cgalParamNEnd +* +* \cgalParamNBegin{use_Delaunay_flips} +* \cgalParamDescription{If `true`, area-based smoothing will be completed by a phase of +* Delaunay-based edge-flips to prevent the creation of elongated triangles.} +* \cgalParamType{Boolean} +* \cgalParamDefault{`true`} +* \cgalParamNEnd +* +* \cgalParamNBegin{do_project} +* \cgalParamDescription{If `true`, points are projected onto the initial surface after each iteration.} +* \cgalParamType{Boolean} +* \cgalParamDefault{`true`} +* \cgalParamNEnd +* +* \cgalParamNBegin{vertex_is_constrained_map} +* \cgalParamDescription{a property map containing the constrained-or-not status of each vertex of `tmesh`.} +* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%vertex_descriptor` +* as key type and `bool` as value type. It must be default constructible.} +* \cgalParamDefault{a default property map where no vertex is constrained} +* \cgalParamExtra{A constrained vertex cannot be modified at all during smoothing.} +* \cgalParamNEnd +* +* \cgalParamNBegin{edge_is_constrained_map} +* \cgalParamDescription{a property map containing the constrained-or-not status of each edge of `tmesh`.} +* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%edge_descriptor` +* as key type and `bool` as value type. It must be default constructible.} +* \cgalParamDefault{a default property map where no edge is constrained} +* \cgalParamExtra{A constrained edge cannot be modified at all during smoothing.} +* \cgalParamNEnd +* \cgalNamedParamsEnd +* +* @warning The third party library \link thirdpartyCeres Ceres \endlink is required +* to use area-based smoothing. +* +* @pre `tmesh` does not contain any degenerate faces +*/ +template +void angle_and_area_smoothing(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; + + // We need a default pmap that is not just 'constant_pmap(false)' because if an edge is constrained, + // its vertices are constrained. + typedef CGAL::dynamic_vertex_property_t Vertex_property_tag; + typedef typename boost::property_map::type Default_VCMap; + typedef typename internal_np::Lookup_named_param_def ::type VCMap; + + typedef typename internal_np::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; + + typedef internal::Angle_smoother Angle_optimizer; + 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; + + using parameters::choose_parameter; + using parameters::get_parameter; + + // named parameters + GeomTraits gt = choose_parameter(get_parameter(np, internal_np::geom_traits)); + VertexPointMap vpmap = choose_parameter(get_parameter(np, internal_np::vertex_point), + get_property_map(CGAL::vertex_point, tmesh)); + + const bool use_angle_smoothing = choose_parameter(get_parameter(np, internal_np::use_angle_smoothing), true); + bool use_area_smoothing = choose_parameter(get_parameter(np, internal_np::use_area_smoothing), true); + +#ifndef CGAL_PMP_USE_CERES_SOLVER + std::cerr << "Area-based smoothing requires the Ceres Library, which is not available." << std::endl; + std::cerr << "No such smoothing will be performed!" << std::endl; + use_area_smoothing = false; +#endif + + if(!use_angle_smoothing && !use_area_smoothing) + std::cerr << "Called PMP::angle_and_area_smoothing() without any smoothing method selected or available" << std::endl; + + unsigned int nb_iterations = choose_parameter(get_parameter(np, internal_np::number_of_iterations), 1); + const bool do_project = choose_parameter(get_parameter(np, internal_np::do_project), true); + const bool use_safety_constraints = choose_parameter(get_parameter(np, internal_np::use_safety_constraints), true); + const bool use_Delaunay_flips = choose_parameter(get_parameter(np, internal_np::use_Delaunay_flips), true); + + VCMap vcmap = choose_parameter(get_parameter(np, internal_np::vertex_is_constrained), + get(Vertex_property_tag(), tmesh)); + + // If it's the default vcmap, manually set everything to false because the dynamic pmap has no default initialization + if((std::is_same::value)) + { + for(vertex_descriptor v : vertices(tmesh)) + put(vcmap, v, false); + } + + ECMap ecmap = choose_parameter(get_parameter(np, internal_np::edge_is_constrained), + Static_boolean_property_map()); + + // a constrained edge has constrained extremities + for(face_descriptor f : faces) + { + if(f == boost::graph_traits::null_face()) + continue; + + for(halfedge_descriptor h : CGAL::halfedges_around_face(halfedge(f, tmesh), tmesh)) + { + if(get(ecmap, edge(h, tmesh))) + { + put(vcmap, source(h, tmesh), true); + put(vcmap, target(h, tmesh), true); + } + } + } + + // 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); + if(is_border(h, tmesh)) // should not happen, but just in case + continue; + + 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()); + + // Setup the working ranges and check some preconditions + Angle_smoother angle_smoother(tmesh, vpmap, vcmap, gt); + Area_smoother area_smoother(tmesh, vpmap, vcmap, gt); + Delaunay_flipper delaunay_flipper(tmesh, vpmap, ecmap, gt); + + if(use_angle_smoothing) + angle_smoother.init_smoothing(faces); + + if(use_area_smoothing) + area_smoother.init_smoothing(faces); + + for(unsigned int i=0; i +void angle_and_area_smoothing(const FaceRange& face_range, TriangleMesh& tmesh) +{ + angle_and_area_smoothing(face_range, tmesh, parameters::all_default()); +} + +template +void angle_and_area_smoothing(TriangleMesh& tmesh, const CGAL_PMP_NP_CLASS& np) +{ + angle_and_area_smoothing(faces(tmesh), tmesh, np); +} + +template +void angle_and_area_smoothing(TriangleMesh& tmesh) +{ + angle_and_area_smoothing(faces(tmesh), tmesh, parameters::all_default()); +} + +template +void angles_evaluation(TriangleMesh& tmesh, GeomTraits traits, Stream& output) +{ + internal::Quality_evaluator evaluator(tmesh, traits); + evaluator.gather_angles(); + evaluator.extract_angles(output); +} + +template +void areas_evaluation(TriangleMesh& tmesh, GeomTraits traits, Stream& output) +{ + internal::Quality_evaluator evaluator(tmesh, traits); + evaluator.measure_areas(); + evaluator.extract_areas(output); +} + +template +void aspect_ratio_evaluation(TriangleMesh& tmesh, GeomTraits traits, Stream& output) +{ + internal::Quality_evaluator evaluator(tmesh, traits); + evaluator.calc_aspect_ratios(); + evaluator.extract_aspect_ratios(output); +} +///\endcond SKIP_IN_MANUAL + +} // namespace Polygon_mesh_processing +} // namespace CGAL + +#endif // CGAL_POLYGON_MESH_PROCESSING_ANGLE_AND_AREA_SMOOTHING_H 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 c054e7feda8..defef9a22e8 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 @@ -16,14 +16,13 @@ #include -#include -#include -#include +#define CGAL_DEPRECATED_HEADER "" +#define CGAL_REPLACEMENT_HEADER "" +#include -#include -#include +#include -#include +#ifndef CGAL_NO_DEPRECATED_CODE #ifdef DOXYGEN_RUNNING #define CGAL_PMP_NP_TEMPLATE_PARAMETERS NamedParameters @@ -34,345 +33,42 @@ namespace CGAL { namespace Polygon_mesh_processing { /*! -* \ingroup PMP_meshing_grp +* \ingroup PMPDeprecated * -* \short smooths a triangulated region of a polygon mesh. -* -* This function attempts to make the triangle angle and area distributions as uniform as possible -* by moving (non-constrained) vertices. -* -* 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. -* -* Optionally, the points are reprojected 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 bgl_namedparameters "Named Parameters" -* -* @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 an optional sequence of \ref bgl_namedparameters "Named Parameters" among the ones listed below -* -* \cgalNamedParamsBegin -* \cgalParamNBegin{number_of_iterations} -* \cgalParamDescription{the number of iterations for the sequence of the smoothing iterations performed} -* \cgalParamType{unsigned int} -* \cgalParamDefault{`1`} -* \cgalParamNEnd -* -* \cgalParamNBegin{use_angle_smoothing} -* \cgalParamDescription{value to indicate whether angle-based smoothing should be used} -* \cgalParamType{Boolean} -* \cgalParamDefault{`true`} -* \cgalParamNEnd -* -* \cgalParamNBegin{use_area_smoothing} -* \cgalParamDescription{value to indicate whether area-based smoothing should be used} -* \cgalParamType{Boolean} -* \cgalParamDefault{`true`} -* \cgalParamNEnd -* -* \cgalParamNBegin{vertex_point_map} -* \cgalParamDescription{a property map associating points to the vertices of `tmesh`} -* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%vertex_descriptor` -* as key type and `%Point_3` as value type} -* \cgalParamDefault{`boost::get(CGAL::vertex_point, tmesh)`} -* \cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t` -* must be available in `TriangleMesh`.} -* \cgalParamNEnd -* -* \cgalParamNBegin{geom_traits} -* \cgalParamDescription{an instance of a geometric traits class} -* \cgalParamType{a class model of `Kernel`} -* \cgalParamDefault{a \cgal Kernel deduced from the point type, using `CGAL::Kernel_traits`} -* \cgalParamExtra{The geometric traits class must be compatible with the vertex point type.} -* \cgalParamNEnd -* -* \cgalParamNBegin{use_safety_constraints} -* \cgalParamDescription{If `true`, vertex moves that would worsen the mesh are ignored.} -* \cgalParamType{Boolean} -* \cgalParamDefault{`false`} -* \cgalParamNEnd -* -* \cgalParamNBegin{use_Delaunay_flips} -* \cgalParamDescription{If `true`, area-based smoothing will be completed by a phase of -* Delaunay-based edge-flips to prevent the creation of elongated triangles.} -* \cgalParamType{Boolean} -* \cgalParamDefault{`true`} -* \cgalParamNEnd -* -* \cgalParamNBegin{do_project} -* \cgalParamDescription{If `true`, points are projected onto the initial surface after each iteration.} -* \cgalParamType{Boolean} -* \cgalParamDefault{`true`} -* \cgalParamNEnd -* -* \cgalParamNBegin{vertex_is_constrained_map} -* \cgalParamDescription{a property map containing the constrained-or-not status of each vertex of `tmesh`.} -* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%vertex_descriptor` -* as key type and `bool` as value type. It must be default constructible.} -* \cgalParamDefault{a default property map where no vertex is constrained} -* \cgalParamExtra{A constrained vertex cannot be modified at all during smoothing.} -* \cgalParamNEnd -* -* \cgalParamNBegin{edge_is_constrained_map} -* \cgalParamDescription{a property map containing the constrained-or-not status of each edge of `tmesh`.} -* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%edge_descriptor` -* as key type and `bool` as value type. It must be default constructible.} -* \cgalParamDefault{a default property map where no edge is constrained} -* \cgalParamExtra{A constrained edge cannot be modified at all during smoothing.} -* \cgalParamNEnd -* \cgalNamedParamsEnd -* -* @warning The third party library \link thirdpartyCeres Ceres \endlink is required -* to use area-based smoothing. -* -* @pre `tmesh` does not contain any degenerate faces +* \deprecated This function is deprecated since \cgal 5.5, +* `CGAL::angle_and_area_smoothing()` should be used instead. */ template -void smooth_mesh(const FaceRange& faces, +CGAL_DEPRECATED void smooth_mesh(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; - - // We need a default pmap that is not just 'constant_pmap(false)' because if an edge is constrained, - // its vertices are constrained. - typedef CGAL::dynamic_vertex_property_t Vertex_property_tag; - typedef typename boost::property_map::type Default_VCMap; - typedef typename internal_np::Lookup_named_param_def ::type VCMap; - - typedef typename internal_np::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; - - typedef internal::Angle_smoother Angle_optimizer; - 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; - - using parameters::choose_parameter; - using parameters::get_parameter; - - // named parameters - GeomTraits gt = choose_parameter(get_parameter(np, internal_np::geom_traits)); - VertexPointMap vpmap = choose_parameter(get_parameter(np, internal_np::vertex_point), - get_property_map(CGAL::vertex_point, tmesh)); - - const bool use_angle_smoothing = choose_parameter(get_parameter(np, internal_np::use_angle_smoothing), true); - bool use_area_smoothing = choose_parameter(get_parameter(np, internal_np::use_area_smoothing), true); - -#ifndef CGAL_PMP_USE_CERES_SOLVER - std::cerr << "Area-based smoothing requires the Ceres Library, which is not available." << std::endl; - std::cerr << "No such smoothing will be performed!" << std::endl; - use_area_smoothing = false; -#endif - - if(!use_angle_smoothing && !use_area_smoothing) - std::cerr << "Called PMP::smooth_mesh() without any smoothing method selected or available" << std::endl; - - unsigned int nb_iterations = choose_parameter(get_parameter(np, internal_np::number_of_iterations), 1); - const bool do_project = choose_parameter(get_parameter(np, internal_np::do_project), true); - const bool use_safety_constraints = choose_parameter(get_parameter(np, internal_np::use_safety_constraints), true); - const bool use_Delaunay_flips = choose_parameter(get_parameter(np, internal_np::use_Delaunay_flips), true); - - VCMap vcmap = choose_parameter(get_parameter(np, internal_np::vertex_is_constrained), - get(Vertex_property_tag(), tmesh)); - - // If it's the default vcmap, manually set everything to false because the dynamic pmap has no default initialization - if((std::is_same::value)) - { - for(vertex_descriptor v : vertices(tmesh)) - put(vcmap, v, false); - } - - ECMap ecmap = choose_parameter(get_parameter(np, internal_np::edge_is_constrained), - Static_boolean_property_map()); - - // a constrained edge has constrained extremities - for(face_descriptor f : faces) - { - if(f == boost::graph_traits::null_face()) - continue; - - for(halfedge_descriptor h : CGAL::halfedges_around_face(halfedge(f, tmesh), tmesh)) - { - if(get(ecmap, edge(h, tmesh))) - { - put(vcmap, source(h, tmesh), true); - put(vcmap, target(h, tmesh), true); - } - } - } - - // 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); - if(is_border(h, tmesh)) // should not happen, but just in case - continue; - - 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()); - - // Setup the working ranges and check some preconditions - Angle_smoother angle_smoother(tmesh, vpmap, vcmap, gt); - Area_smoother area_smoother(tmesh, vpmap, vcmap, gt); - Delaunay_flipper delaunay_flipper(tmesh, vpmap, ecmap, gt); - - if(use_angle_smoothing) - angle_smoother.init_smoothing(faces); - - if(use_area_smoothing) - area_smoother.init_smoothing(faces); - - for(unsigned int i=0; i -void smooth_mesh(const FaceRange& face_range, TriangleMesh& tmesh) +CGAL_DEPRECATED void smooth_mesh(const FaceRange& face_range, TriangleMesh& tmesh) { smooth_mesh(face_range, tmesh, parameters::all_default()); } template -void smooth_mesh(TriangleMesh& tmesh, const CGAL_PMP_NP_CLASS& np) +CGAL_DEPRECATED void smooth_mesh(TriangleMesh& tmesh, const CGAL_PMP_NP_CLASS& np) { smooth_mesh(faces(tmesh), tmesh, np); } template -void smooth_mesh(TriangleMesh& tmesh) +CGAL_DEPRECATED void smooth_mesh(TriangleMesh& tmesh) { smooth_mesh(faces(tmesh), tmesh, parameters::all_default()); } - -template -void angles_evaluation(TriangleMesh& tmesh, GeomTraits traits, Stream& output) -{ - internal::Quality_evaluator evaluator(tmesh, traits); - evaluator.gather_angles(); - evaluator.extract_angles(output); -} - -template -void areas_evaluation(TriangleMesh& tmesh, GeomTraits traits, Stream& output) -{ - internal::Quality_evaluator evaluator(tmesh, traits); - evaluator.measure_areas(); - evaluator.extract_areas(output); -} - -template -void aspect_ratio_evaluation(TriangleMesh& tmesh, GeomTraits traits, Stream& output) -{ - internal::Quality_evaluator evaluator(tmesh, traits); - evaluator.calc_aspect_ratios(); - evaluator.extract_aspect_ratios(output); -} ///\endcond SKIP_IN_MANUAL } // namespace Polygon_mesh_processing } // namespace CGAL +#endif //#ifndef CGAL_NO_DEPRECATED_CODE + #endif // CGAL_POLYGON_MESH_PROCESSING_SMOOTH_MESH_H 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 fa1d90d01e1..ddcbae7905e 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 @@ -2,7 +2,7 @@ #include #include -#include +#include #include #include @@ -37,8 +37,8 @@ void test_smoothing(const char* filename) Mesh mesh; read_mesh(filename, mesh); - PMP::smooth_mesh(mesh); - PMP::smooth_mesh(mesh, CGAL::parameters::number_of_iterations(10)); + PMP::angle_and_area_smoothing(mesh); + PMP::angle_and_area_smoothing(mesh, CGAL::parameters::number_of_iterations(10)); } template @@ -47,8 +47,8 @@ void test_angle_smoothing(const char* filename) Mesh mesh; read_mesh(filename, mesh); - PMP::smooth_mesh(mesh); - PMP::smooth_mesh(mesh, CGAL::parameters::number_of_iterations(10) + PMP::angle_and_area_smoothing(mesh); + PMP::angle_and_area_smoothing(mesh, CGAL::parameters::number_of_iterations(10) .use_area_smoothing(false)); } @@ -58,8 +58,8 @@ void test_area_smoothing(const char* filename) Mesh mesh; read_mesh(filename, mesh); - PMP::smooth_mesh(mesh); - PMP::smooth_mesh(mesh, CGAL::parameters::number_of_iterations(10) + PMP::angle_and_area_smoothing(mesh); + PMP::angle_and_area_smoothing(mesh, CGAL::parameters::number_of_iterations(10) .use_angle_smoothing(false)); } @@ -69,7 +69,7 @@ void test_angle_smoothing_without_projection(const char* filename) Mesh mesh; read_mesh(filename, mesh); - PMP::smooth_mesh(mesh, CGAL::parameters::do_project(false) + PMP::angle_and_area_smoothing(mesh, CGAL::parameters::do_project(false) .use_area_smoothing(false)); } @@ -79,7 +79,7 @@ void test_area_smoothing_without_projection(const char* filename) Mesh mesh; read_mesh(filename, mesh); - PMP::smooth_mesh(mesh, CGAL::parameters::do_project(false) + PMP::angle_and_area_smoothing(mesh, CGAL::parameters::do_project(false) .use_angle_smoothing(false)); } @@ -117,7 +117,7 @@ void test_constrained_vertices(const char* filename) CGAL::Boolean_property_map > vcmap(selected_vertices); - PMP::smooth_mesh(mesh, CGAL::parameters::vertex_is_constrained_map(vcmap)); + PMP::angle_and_area_smoothing(mesh, CGAL::parameters::vertex_is_constrained_map(vcmap)); for(vertex_descriptor v : vertices(mesh)) { From 9f5f5aa7a786ffa0d4b94407e15e59ed9ecb70ff Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Mon, 27 Sep 2021 17:24:24 +0200 Subject: [PATCH 20/46] use angle_and_area_smoothing and remove the use of deprecated smooth_mesh() --- .../demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp index 060711365df..9d306cc5e7b 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp @@ -8,7 +8,7 @@ #include #include -#include +#include #include #include @@ -278,7 +278,7 @@ public Q_SLOTS: if(poly_item) { - smooth_mesh(pmesh, parameters::do_project(projection) + angle_and_area_smoothing(pmesh, parameters::do_project(projection) .number_of_iterations(nb_iter) .vertex_is_constrained_map(vcmap) .use_safety_constraints(use_safety_measures) @@ -296,7 +296,7 @@ public Q_SLOTS: // No faces selected --> use all faces if(std::begin(selection_item->selected_facets) == std::end(selection_item->selected_facets)) { - smooth_mesh(pmesh, parameters::do_project(projection) + angle_and_area_smoothing(pmesh, parameters::do_project(projection) .number_of_iterations(nb_iter) .vertex_is_constrained_map(vcmap) .edge_is_constrained_map(selection_item->constrained_edges_pmap()) @@ -307,7 +307,7 @@ public Q_SLOTS: } else // some faces exist in the selection { - smooth_mesh(selection_item->selected_facets, pmesh, parameters::do_project(projection) + angle_and_area_smoothing(selection_item->selected_facets, pmesh, parameters::do_project(projection) .number_of_iterations(nb_iter) .vertex_is_constrained_map(vcmap) .edge_is_constrained_map(selection_item->constrained_edges_pmap()) From 0a60485049ccccb8079e1ef4268354c08849d0f4 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Mon, 27 Sep 2021 17:38:01 +0200 Subject: [PATCH 21/46] user manual --- .../Polygon_mesh_processing.txt | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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 92b442267db..bac2ea3e0a1 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 @@ -138,7 +138,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_mesh()` moves vertices to optimize geometry around each vertex: +- Mesh smoothing by angle and area optimization : +`CGAL::Polygon_mesh_processing::angle_and_area_smoothing()` 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}. @@ -166,14 +167,14 @@ Statistics for the various combinations of mesh smoothing. \cgalFigureEnd - Mesh smoothing by tangential relaxation : `CGAL::Polygon_mesh_processing::tangential_relaxation()` -iteratively performs vertex relocations following an area-based tangential smoothing scheme described in -\cgalCite{botsch2010PMP}. +moves vertices following an area-based Laplacian smoothing scheme, performed +at each vertex in an estimated tangent plane to the surface. +The full algorithm is described in \cgalCite{botsch2010PMP}. The example \ref Polygon_mesh_processing/tangential_relaxation_example.cpp shows how this mesh relaxation function can be used. - - Shape smoothing: `CGAL::Polygon_mesh_processing::smooth_shape()` -incrementally moves vertices towards a weighted barycenter of their neighbors along the mean curvature flow. +moves vertices towards a weighted barycenter of their neighbors along the mean curvature flow. The curvature flow algorithm for shape smoothing is based on Desbrun et al. \cgalCite{cgal:dmsb-ifamdcf-99} and on Kazhdan et al. \cgalCite{kazhdan2012can}. The algorithm uses the mean curvature flow to calculate the translation of vertices along the surface normal with a speed equal to the mean curvature of the area that is being smoothed. This means that vertices on sharp corners slide faster. From 276ea183132f64c3bad9898a0d6e5370330df1b8 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 5 Oct 2021 11:12:49 +0200 Subject: [PATCH 22/46] formatting (Mael's review) --- .../Polygon_mesh_processing.txt | 4 ++-- .../mesh_smoothing_example.cpp | 4 ++-- .../angle_and_area_smoothing.h | 6 +++--- .../internal/Isotropic_remeshing/remesh_impl.h | 17 +++++++++-------- .../test_mesh_smoothing.cpp | 14 ++++++++------ 5 files changed, 24 insertions(+), 21 deletions(-) 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 bac2ea3e0a1..97a053ec58f 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 @@ -138,7 +138,7 @@ 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 by angle and area optimization : +- Mesh smoothing by angle and area optimization: `CGAL::Polygon_mesh_processing::angle_and_area_smoothing()` 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. @@ -166,7 +166,7 @@ Mesh smoothing of the closed surface blobby, containing self-intersection Statistics for the various combinations of mesh smoothing. \cgalFigureEnd -- Mesh smoothing by tangential relaxation : `CGAL::Polygon_mesh_processing::tangential_relaxation()` +- Mesh smoothing by tangential relaxation: `CGAL::Polygon_mesh_processing::tangential_relaxation()` moves vertices following an area-based Laplacian smoothing scheme, performed at each vertex in an estimated tangent plane to the surface. The full algorithm is described in \cgalCite{botsch2010PMP}. 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 e78befe50da..2f2a6c01b43 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 @@ -44,8 +44,8 @@ int main(int argc, char** argv) // Smooth with both angle and area criteria + Delaunay flips PMP::angle_and_area_smoothing(mesh, PMP::parameters::number_of_iterations(nb_iterations) - .use_safety_constraints(false) // authorize all moves - .edge_is_constrained_map(eif)); + .use_safety_constraints(false) // authorize all moves + .edge_is_constrained_map(eif)); CGAL::IO::write_polygon_mesh("mesh_smoothed.off", mesh, CGAL::parameters::stream_precision(17)); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/angle_and_area_smoothing.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/angle_and_area_smoothing.h index a20437ba1c8..e8de9265d71 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/angle_and_area_smoothing.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/angle_and_area_smoothing.h @@ -132,12 +132,12 @@ namespace Polygon_mesh_processing { * @warning The third party library \link thirdpartyCeres Ceres \endlink is required * to use area-based smoothing. * -* @pre `tmesh` does not contain any degenerate faces +* @pre `tmesh` does not contain any degenerate faces. */ template void angle_and_area_smoothing(const FaceRange& faces, - TriangleMesh& tmesh, - const NamedParameters& np) + TriangleMesh& tmesh, + const NamedParameters& np) { typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index f45383d7555..eca5102cbe9 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -1026,14 +1026,15 @@ namespace internal { auto constrained_vertices_pmap = boost::make_function_property_map(vertex_constraint); - PMP::tangential_relaxation(vertices(mesh_), - mesh_, - CGAL::parameters::number_of_iterations(nb_iterations) - .vertex_point_map(vpmap_) - .geom_traits(gt_) - .edge_is_constrained_map(constrained_edges_pmap) - .vertex_is_constrained_map(constrained_vertices_pmap) - .relax_constraints(relax_constraints) + PMP::tangential_relaxation( + vertices(mesh_), + mesh_, + CGAL::parameters::number_of_iterations(nb_iterations) + .vertex_point_map(vpmap_) + .geom_traits(gt_) + .edge_is_constrained_map(constrained_edges_pmap) + .vertex_is_constrained_map(constrained_vertices_pmap) + .relax_constraints(relax_constraints) ); CGAL_assertion(!input_mesh_is_valid_ || is_valid_polygon_mesh(mesh_)); 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 ddcbae7905e..8d807e01cc1 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 @@ -49,7 +49,7 @@ void test_angle_smoothing(const char* filename) PMP::angle_and_area_smoothing(mesh); PMP::angle_and_area_smoothing(mesh, CGAL::parameters::number_of_iterations(10) - .use_area_smoothing(false)); + .use_area_smoothing(false)); } template @@ -60,7 +60,7 @@ void test_area_smoothing(const char* filename) PMP::angle_and_area_smoothing(mesh); PMP::angle_and_area_smoothing(mesh, CGAL::parameters::number_of_iterations(10) - .use_angle_smoothing(false)); + .use_angle_smoothing(false)); } template @@ -70,7 +70,7 @@ void test_angle_smoothing_without_projection(const char* filename) read_mesh(filename, mesh); PMP::angle_and_area_smoothing(mesh, CGAL::parameters::do_project(false) - .use_area_smoothing(false)); + .use_area_smoothing(false)); } template @@ -80,7 +80,7 @@ void test_area_smoothing_without_projection(const char* filename) read_mesh(filename, mesh); PMP::angle_and_area_smoothing(mesh, CGAL::parameters::do_project(false) - .use_angle_smoothing(false)); + .use_angle_smoothing(false)); } template @@ -88,9 +88,11 @@ void test_tangential_relaxation(const char* filename) { Mesh mesh; read_mesh(filename, mesh); - PMP::tangential_relaxation(vertices(mesh), mesh, + PMP::tangential_relaxation( + vertices(mesh), + mesh, CGAL::parameters::number_of_iterations(4) - .relax_constraints(false)); + .relax_constraints(false)); PMP::tangential_relaxation(vertices(mesh), mesh); PMP::tangential_relaxation(mesh); } From bbd53e56647feff68656dd917aded6088e27eb32 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 5 Oct 2021 15:14:52 +0200 Subject: [PATCH 23/46] doc (review Mael) --- .../CGAL/Polygon_mesh_processing/remesh.h | 2 +- .../tangential_relaxation.h | 17 ++++++++--------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h index b011b2d61b6..d31e094fbe0 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -159,7 +159,7 @@ namespace Polygon_mesh_processing { * * \cgalParamNBegin{relax_constraints} * \cgalParamDescription{If `true`, the end vertices of the edges set as constrained -* in `edge_is_constrained_map` and boundary edges move along the} +* in `edge_is_constrained_map` and boundary edges move along the * constrained polylines they belong to.} * \cgalParamType{Boolean} * \cgalParamDefault{`false`} diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index 90a1bcf1e88..28f89bf691f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -72,32 +72,31 @@ namespace Polygon_mesh_processing { * \cgalParamNEnd * * \cgalParamNBegin{number_of_iterations} -* \cgalParamDescription{the number of iterations for the sequence of atomic operations performed (listed in the above description)} +* \cgalParamDescription{the number of iterations smoothing iterations} * \cgalParamType{unsigned int} * \cgalParamDefault{`1`} * \cgalParamNEnd * * \cgalParamNBegin{edge_is_constrained_map} -* \cgalParamDescription{a property map containing the constrained-or-not status of each edge of `tm`} +* \cgalParamDescription{a property map containing the constrained-or-not status of each edge of `tm`. +* The endpoints of a constrained edge cannot be moved by relaxation.} * \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%edge_descriptor` * as key type and `bool` as value type. It must be default constructible.} -* \cgalParamDefault{a default property map where no edge is constrained} -* \cgalParamExtra{A constrained edge can be split or collapsed, but not flipped, nor its endpoints moved by smoothing.} -* \cgalParamExtra{Sub-edges generated by splitting are set to be constrained.} +* \cgalParamDefault{a default property map where no edges are constrained} * \cgalParamExtra{Patch boundary edges (i.e. incident to only one face in the range) are always considered as constrained edges.} * \cgalParamNEnd * * \cgalParamNBegin{vertex_is_constrained_map} -* \cgalParamDescription{a property map containing the constrained-or-not status of each vertex of `tm`.} +* \cgalParamDescription{a property map containing the constrained-or-not status of each vertex of `tm`. +* A constrained vertex cannot be modified during relaxation.} * \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%vertex_descriptor` * as key type and `bool` as value type. It must be default constructible.} -* \cgalParamDefault{a default property map where no vertex is constrained} -* \cgalParamExtra{A constrained vertex cannot be modified during remeshing.} +* \cgalParamDefault{a default property map where no vertices are constrained} * \cgalParamNEnd * * \cgalParamNBegin{relax_constraints} * \cgalParamDescription{If `true`, the end vertices of the edges set as constrained -* in `edge_is_constrained_map` and boundary edges move along the} +* in `edge_is_constrained_map` and boundary edges move along the * constrained polylines they belong to.} * \cgalParamType{Boolean} * \cgalParamDefault{`false`} From 6268b854322e30a24d238da44f09195556328f1a Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 5 Oct 2021 15:47:05 +0200 Subject: [PATCH 24/46] various efficiency improvements --- .../tangential_relaxation.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index 28f89bf691f..a7d4a44c917 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -157,7 +157,7 @@ namespace Polygon_mesh_processing { { if (is_border(hd, tm)) continue; - Vector_3 n = compute_face_normal(face(hd, tm), tm); + Vector_3 n = compute_face_normal(face(hd, tm), tm, np); if (n == CGAL::NULL_VECTOR) //for degenerate faces continue; @@ -225,9 +225,9 @@ namespace Polygon_mesh_processing { ++star_size; } CGAL_assertion(star_size > 0); //isolated vertices have already been discarded - move = (1. / (double)star_size) * move; + move = (1. / static_cast(star_size)) * move; - barycenters.push_back( VNP(v, vn, get(vpm, v) + move) ); + barycenters.emplace_back(v, vn, get(vpm, v) + move); } else { @@ -244,7 +244,7 @@ namespace Polygon_mesh_processing { //check squared cosine is < 0.25 (~120 degrees) if (0.25 < dot*dot / ( squared_distance(get(vpm,ph0), get(vpm, v)) * squared_distance(get(vpm,ph1), get(vpm, v))) ) - barycenters.push_back( VNP(v, vn, barycenter(get(vpm, ph0), 0.25, get(vpm, ph1), 0.25, get(vpm, v), 0.5)) ); + barycenters.emplace_back(v, vn, barycenter(get(vpm, ph0), 0.25, get(vpm, ph1), 0.25, get(vpm, v), 0.5)); } } } @@ -256,17 +256,17 @@ namespace Polygon_mesh_processing { for(const VNP& vnp : barycenters) { vertex_descriptor v = std::get<0>(vnp); - Point_3 pv = get(vpm, v); + const Point_3& pv = get(vpm, v); const Vector_3& nv = std::get<1>(vnp); const Point_3& qv = std::get<2>(vnp); //barycenter at v - new_locations.push_back( std::make_pair(v, qv + (nv * Vector_3(qv, pv)) * nv) ); + new_locations.emplace_back(v, qv + (nv * Vector_3(qv, pv)) * nv); } // perform moves for(const VP_pair& vp : new_locations) { - const Point_3 initial_pos = get(vpm, vp.first); + const Point_3& initial_pos = get(vpm, vp.first); const Vector_3 move(initial_pos, vp.second); put(vpm, vp.first, vp.second); From 0b757b8c30cb963fba2c9d3f02557ed447c9e779 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 5 Oct 2021 17:04:03 +0200 Subject: [PATCH 25/46] avoid computing normals multiple times --- .../tangential_relaxation.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index a7d4a44c917..5f0cb236701 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -24,6 +24,7 @@ #include #include +#include #ifdef DOXYGEN_RUNNING #define CGAL_PMP_NP_TEMPLATE_PARAMETERS NamedParameters @@ -192,16 +193,19 @@ namespace Polygon_mesh_processing { typedef std::tuple VNP; std::vector< VNP > barycenters; + // at each vertex, compute vertex normal + std::unordered_map vnormals; + compute_vertex_normals(tm, boost::make_assoc_property_map(vnormals), np); + // at each vertex, compute barycenter of neighbors for(vertex_descriptor v : vertices) { if (get(vcm, v) || halfedge(v, tm)==boost::graph_traits::null_halfedge()) continue; - std::vector interior_hedges, border_halfedges; - // collect hedges to detect if we have to handle boundary cases + std::vector interior_hedges, border_halfedges; for(halfedge_descriptor h : halfedges_around_target(v, tm)) { if (is_border_edge(h, tm) || get(ecm, edge(h, tm))) @@ -212,11 +216,7 @@ namespace Polygon_mesh_processing { if (border_halfedges.empty()) { - // \todo: shall we want to have a way to compute once for all vertices (per loop) - // this would avoid recompute face normals - Vector_3 vn = compute_vertex_normal(v, tm, - parameters::vertex_point_map(vpm) - .geom_traits(gt)); + const Vector_3& vn = vnormals.at(v); Vector_3 move = CGAL::NULL_VECTOR; unsigned int star_size = 0; for(halfedge_descriptor h :interior_hedges) From 1e334366f77cd90ac037bed07c596f11ac9753da Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 5 Oct 2021 17:04:13 +0200 Subject: [PATCH 26/46] improve example --- .../tangential_relaxation_example.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp index d5ac513e7ed..f92cfe45123 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp @@ -24,12 +24,11 @@ int main(int argc, char* argv[]) } double target_edge_length = (argc > 2) ? std::stod(std::string(argv[2])) : 0.04; - unsigned int nb_iter = 3; + unsigned int nb_iter = (argc > 3) ? std::stoi(std::string(argv[3])) : 10; std::cout << "Relax..."; - PMP::tangential_relaxation(mesh, - CGAL::parameters::number_of_iterations(3)); + PMP::tangential_relaxation(mesh, CGAL::parameters::number_of_iterations(nb_iter)); std::cout << "done." << std::endl; From 52d9a2f7f8021ea4b34c13015840161d6dc51855 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Thu, 16 Dec 2021 16:05:47 +0100 Subject: [PATCH 27/46] replace PolygonMesh by TriangleMesh in doc Co-authored-by: Mael --- .../CGAL/Polygon_mesh_processing/tangential_relaxation.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index 5f0cb236701..c5289bb6d83 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -57,11 +57,11 @@ namespace Polygon_mesh_processing { * \cgalNamedParamsBegin * \cgalParamNBegin{vertex_point_map} * \cgalParamDescription{a property map associating points to the vertices of `tm`} -* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%vertex_descriptor` +* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%vertex_descriptor` * as key type and `%Point_3` as value type} * \cgalParamDefault{`boost::get(CGAL::vertex_point, tm)`} * \cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t` -* must be available in `PolygonMesh`.} +* must be available in `TriangleMesh`.} * \cgalParamNEnd * * \cgalParamNBegin{geom_traits} @@ -81,7 +81,7 @@ namespace Polygon_mesh_processing { * \cgalParamNBegin{edge_is_constrained_map} * \cgalParamDescription{a property map containing the constrained-or-not status of each edge of `tm`. * The endpoints of a constrained edge cannot be moved by relaxation.} -* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%edge_descriptor` +* \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%edge_descriptor` * as key type and `bool` as value type. It must be default constructible.} * \cgalParamDefault{a default property map where no edges are constrained} * \cgalParamExtra{Patch boundary edges (i.e. incident to only one face in the range) are always considered as constrained edges.} From a897bbb71e28fca99ca7518ab166306aa2384113 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Thu, 16 Dec 2021 16:12:57 +0100 Subject: [PATCH 28/46] improve doc --- .../CGAL/Polygon_mesh_processing/tangential_relaxation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index c5289bb6d83..eb7dd6ce143 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -84,7 +84,7 @@ namespace Polygon_mesh_processing { * \cgalParamType{a class model of `ReadWritePropertyMap` with `boost::graph_traits::%edge_descriptor` * as key type and `bool` as value type. It must be default constructible.} * \cgalParamDefault{a default property map where no edges are constrained} -* \cgalParamExtra{Patch boundary edges (i.e. incident to only one face in the range) are always considered as constrained edges.} +* \cgalParamExtra{Boundary edges are always considered as constrained edges.} * \cgalParamNEnd * * \cgalParamNBegin{vertex_is_constrained_map} From ef2c3b022dc44a2e41f718f0feda77812eb5fd44 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 28 Dec 2021 14:45:53 +0100 Subject: [PATCH 29/46] update CHANGES.md --- Installation/CHANGES.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index da44a890a15..1ea699331b5 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -1,5 +1,14 @@ Release History =============== + +[Release 5.5](https://github.com/CGAL/cgal/releases/tag/v5.5) +----------- + +### [Polygon Mesh Processing](https://doc.cgal.org/5.5/Manual/packages.html#PkgPolygonMeshProcessing) + +- Added the function `CGAL::Polygon_mesh_processing::tangential_relaxation()`, which +applies an area-based tangential mesh smoothing to a surface triangle mesh vertices. + [Release 5.4](https://github.com/CGAL/cgal/releases/tag/v5.4) ----------- From 40d5b3daeff7b753b66698ca6e41ad49ab5eae8d Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 28 Dec 2021 14:51:39 +0100 Subject: [PATCH 30/46] face graph does not need to be mutable, only the vertex point map --- .../CGAL/Polygon_mesh_processing/tangential_relaxation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index eb7dd6ce143..3607a60452f 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -42,7 +42,7 @@ namespace Polygon_mesh_processing { * is projected back to the tangent plane to the surface at `v`, iteratively. * The connectivity remains unchanged. * -* @tparam TriangleMesh model of `MutableFaceGraph`. +* @tparam TriangleMesh model of `FaceGraph`. * The descriptor types `boost::graph_traits::%face_descriptor` * and `boost::graph_traits::%halfedge_descriptor` must be * models of `Hashable`. From 3df4f974073c54bc5ac0fb70b6945a041eb1a5cc Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 28 Dec 2021 15:48:16 +0100 Subject: [PATCH 31/46] function face() also is needed --- .../CGAL/Polygon_mesh_processing/tangential_relaxation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index 3607a60452f..4fd8665b555 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -42,7 +42,7 @@ namespace Polygon_mesh_processing { * is projected back to the tangent plane to the surface at `v`, iteratively. * The connectivity remains unchanged. * -* @tparam TriangleMesh model of `FaceGraph`. +* @tparam TriangleMesh model of `FaceGraph` and `VertexListGraph`. * The descriptor types `boost::graph_traits::%face_descriptor` * and `boost::graph_traits::%halfedge_descriptor` must be * models of `Hashable`. From b2818f0bf4126da6af96b3e810d21b9b8e30cd3d Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 28 Dec 2021 17:25:59 +0100 Subject: [PATCH 32/46] fix unused parameter warning --- .../include/CGAL/Polygon_mesh_processing/tangential_relaxation.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index 4fd8665b555..47ac7dec801 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -120,7 +120,6 @@ namespace Polygon_mesh_processing { using parameters::choose_parameter; typedef typename GetGeomTraits::type GT; - GT gt = choose_parameter(get_parameter(np, internal_np::geom_traits)); typedef typename GetVertexPointMap::type VPMap; VPMap vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), From e0910f4d72b66e09f83d90311a914fd347acb211 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 4 Jan 2022 15:58:20 +0100 Subject: [PATCH 33/46] fix input type --- .../test/Polygon_mesh_processing/test_mesh_smoothing.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 2f44aac258d..b84f0c57a2f 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 @@ -84,7 +84,7 @@ void test_area_smoothing_without_projection(const std::string filename) } template -void test_tangential_relaxation(const char* filename) +void test_tangential_relaxation(const std::string filename) { Mesh mesh; read_mesh(filename, mesh); From 678b28ec2302c4c2ebebbb3ab3b6984684ee2fd7 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Tue, 4 Jan 2022 16:16:16 +0100 Subject: [PATCH 34/46] avoid using deprecated header and function --- .../repair_self_intersections.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_self_intersections.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_self_intersections.h index e6ad38f591f..34f267b7950 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_self_intersections.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_self_intersections.h @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include #ifndef CGAL_PMP_REMOVE_SELF_INTERSECTION_NO_POLYHEDRAL_ENVELOPE_CHECK #include @@ -481,9 +481,11 @@ bool remove_self_intersections_with_smoothing(std::set Date: Wed, 19 Jan 2022 20:50:26 +0100 Subject: [PATCH 35/46] fixes after merge --- .../angle_and_area_smoothing.h | 27 +++----------- .../tangential_relaxation.h | 36 +++++-------------- 2 files changed, 13 insertions(+), 50 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/angle_and_area_smoothing.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/angle_and_area_smoothing.h index e8de9265d71..b34fb9331cf 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/angle_and_area_smoothing.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/angle_and_area_smoothing.h @@ -20,16 +20,11 @@ #include #include -#include +#include #include #include -#ifdef DOXYGEN_RUNNING -#define CGAL_PMP_NP_TEMPLATE_PARAMETERS NamedParameters -#define CGAL_PMP_NP_CLASS NamedParameters -#endif - namespace CGAL { namespace Polygon_mesh_processing { @@ -134,10 +129,10 @@ namespace Polygon_mesh_processing { * * @pre `tmesh` does not contain any degenerate faces. */ -template +template void angle_and_area_smoothing(const FaceRange& faces, TriangleMesh& tmesh, - const NamedParameters& np) + const NamedParameters& np = parameters::default_values()) { typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; @@ -329,24 +324,12 @@ void angle_and_area_smoothing(const FaceRange& faces, } ///\cond SKIP_IN_MANUAL -template -void angle_and_area_smoothing(const FaceRange& face_range, TriangleMesh& tmesh) -{ - angle_and_area_smoothing(face_range, tmesh, parameters::all_default()); -} - -template -void angle_and_area_smoothing(TriangleMesh& tmesh, const CGAL_PMP_NP_CLASS& np) +template +void angle_and_area_smoothing(TriangleMesh& tmesh, const CGAL_NP_CLASS& np = parameters::default_values()) { angle_and_area_smoothing(faces(tmesh), tmesh, np); } -template -void angle_and_area_smoothing(TriangleMesh& tmesh) -{ - angle_and_area_smoothing(faces(tmesh), tmesh, parameters::all_default()); -} - template void angles_evaluation(TriangleMesh& tmesh, GeomTraits traits, Stream& output) { diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index 47ac7dec801..7b64311a1a8 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -19,19 +19,13 @@ #include #include -#include +#include #include #include #include #include -#ifdef DOXYGEN_RUNNING -#define CGAL_PMP_NP_TEMPLATE_PARAMETERS NamedParameters -#define CGAL_PMP_NP_CLASS NamedParameters -#endif - - namespace CGAL { namespace Polygon_mesh_processing { @@ -107,11 +101,11 @@ namespace Polygon_mesh_processing { * * \todo check if it should really be a triangle mesh or if a polygon mesh is fine */ - template - void tangential_relaxation(const VertexRange& vertices, - TriangleMesh& tm, - const NamedParameters& np) - { +template +void tangential_relaxation(const VertexRange& vertices, + TriangleMesh& tm, + const NamedParameters& np = parameters::default_values()) +{ 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; @@ -289,31 +283,17 @@ namespace Polygon_mesh_processing { #endif } -template -void tangential_relaxation(const VertexRange& vertices, TriangleMesh& tm) -{ - tangential_relaxation(vertices, tm, parameters::all_default()); -} - /*! * \ingroup PMP_meshing_grp * applies `tangential_relaxation()` to all the vertices of `tm`. */ template -void tangential_relaxation(TriangleMesh& tm, const CGAL_PMP_NP_CLASS& np) + typename CGAL_NP_TEMPLATE_PARAMETERS> +void tangential_relaxation(TriangleMesh& tm, const CGAL_NP_CLASS& np = parameters::default_values()) { tangential_relaxation(vertices(tm), tm, np); } -template -void tangential_relaxation(TriangleMesh& tm) -{ - tangential_relaxation(vertices(tm), tm, parameters::all_default()); -} - - - } } // CGAL::Polygon_mesh_processing #endif //CGAL_POLYGON_MESH_PROCESSING_TANGENTIAL_RELAXATION_H From c3fa3f59939d51b68c4eab24293d23ef9027c1b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 20 Jan 2022 08:55:43 +0100 Subject: [PATCH 36/46] fix data path --- .../Polygon_mesh_processing/tangential_relaxation_example.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp index f92cfe45123..e764b27c69a 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp @@ -14,7 +14,7 @@ namespace PMP = CGAL::Polygon_mesh_processing; int main(int argc, char* argv[]) { - const char* filename = (argc > 1) ? argv[1] : "data/pig.off"; + const std::string filename = (argc > 1) ? argv[1] : CGAL::data_file_path("meshes/pig.off"); Mesh mesh; if(!PMP::IO::read_polygon_mesh(filename, mesh) || !CGAL::is_triangle_mesh(mesh)) From ebb699b2c496a80187f7aa59747f414816fa4abe Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Thu, 3 Feb 2022 10:42:07 +0100 Subject: [PATCH 37/46] fix conversion warning --- Classification/examples/Classification/gis_tutorial_example.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Classification/examples/Classification/gis_tutorial_example.cpp b/Classification/examples/Classification/gis_tutorial_example.cpp index 7e214e481f5..e02d128a307 100644 --- a/Classification/examples/Classification/gis_tutorial_example.cpp +++ b/Classification/examples/Classification/gis_tutorial_example.cpp @@ -670,7 +670,7 @@ int main (int argc, char** argv) } std::size_t nb_vertices - = std::accumulate (polylines.begin(), polylines.end(), 0u, + = std::accumulate (polylines.begin(), polylines.end(), std::size_t(0), [](std::size_t size, const std::vector& poly) -> std::size_t { return size + poly.size(); }); From dbe2cda53870981f8866d8b24262fd1a79ec1725 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 23 Mar 2022 22:41:29 +0100 Subject: [PATCH 38/46] fix compilation issue --- .../Isotropic_remeshing/remesh_impl.h | 22 +++++++++---------- .../CGAL/Polygon_mesh_processing/remesh.h | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index dac7e6f0133..12d0a273651 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -983,8 +983,8 @@ namespace internal { // "applies an iterative smoothing filter to the mesh. // The vertex movement has to be constrained to the vertex tangent plane [...] // smoothing algorithm with uniform Laplacian weights" - void tangential_relaxation(const bool relax_constraints/*1d smoothing*/ - , const unsigned int nb_iterations) + void tangential_relaxation_impl(const bool relax_constraints/*1d smoothing*/ + , const unsigned int nb_iterations) { #ifdef CGAL_PMP_REMESHING_VERBOSE std::cout << "Tangential relaxation (" << nb_iterations << " iter.)..."; @@ -1015,15 +1015,15 @@ namespace internal { auto constrained_vertices_pmap = boost::make_function_property_map(vertex_constraint); - PMP::tangential_relaxation( - vertices(mesh_), - mesh_, - CGAL::parameters::number_of_iterations(nb_iterations) - .vertex_point_map(vpmap_) - .geom_traits(gt_) - .edge_is_constrained_map(constrained_edges_pmap) - .vertex_is_constrained_map(constrained_vertices_pmap) - .relax_constraints(relax_constraints) + tangential_relaxation( + vertices(mesh_), + mesh_, + CGAL::parameters::number_of_iterations(nb_iterations) + .vertex_point_map(vpmap_) + .geom_traits(gt_) + .edge_is_constrained_map(constrained_edges_pmap) + .vertex_is_constrained_map(constrained_vertices_pmap) + .relax_constraints(relax_constraints) ); CGAL_assertion(!input_mesh_is_valid_ || is_valid_polygon_mesh(mesh_)); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h index 882b1623e3d..f35236909c3 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -321,7 +321,7 @@ void isotropic_remeshing(const FaceRange& faces } if(do_flip) remesher.flip_edges_for_valence_and_shape(); - remesher.tangential_relaxation(smoothing_1d, nb_laplacian); + remesher.tangential_relaxation_impl(smoothing_1d, nb_laplacian); if ( choose_parameter(get_parameter(np, internal_np::do_project), true) ) remesher.project_to_surface(get_parameter(np, internal_np::projection_functor)); #ifdef CGAL_PMP_REMESHING_VERBOSE From f4098ebe6569d30dbef829f98faffdd9f63db6b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 23 Mar 2022 22:55:47 +0100 Subject: [PATCH 39/46] fix warning --- .../Polygon_mesh_processing/tangential_relaxation_example.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp index e764b27c69a..c915e2ea419 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/tangential_relaxation_example.cpp @@ -23,8 +23,7 @@ int main(int argc, char* argv[]) return 1; } - double target_edge_length = (argc > 2) ? std::stod(std::string(argv[2])) : 0.04; - unsigned int nb_iter = (argc > 3) ? std::stoi(std::string(argv[3])) : 10; + unsigned int nb_iter = (argc > 2) ? std::stoi(std::string(argv[2])) : 10; std::cout << "Relax..."; From 1b5632fcdc9d0baf642d8e986e994ac97dc2168d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 23 Mar 2022 22:56:23 +0100 Subject: [PATCH 40/46] do not care a ref as it will be update to the new position --- .../CGAL/Polygon_mesh_processing/tangential_relaxation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index 7b64311a1a8..d73ddc100b2 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -259,7 +259,7 @@ void tangential_relaxation(const VertexRange& vertices, // perform moves for(const VP_pair& vp : new_locations) { - const Point_3& initial_pos = get(vpm, vp.first); + const Point_3 initial_pos = get(vpm, vp.first); // make a copy on purpose const Vector_3 move(initial_pos, vp.second); put(vpm, vp.first, vp.second); From 66a893d903fcc762c4beae7bd0142158f038b5dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 23 Mar 2022 23:17:48 +0100 Subject: [PATCH 41/46] fix warnings --- .../demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp index 9d306cc5e7b..8b08e92b62b 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Smoothing_plugin.cpp @@ -134,8 +134,7 @@ public: } Ui::Tangential_relaxation_dialog - relaxation_dialog(QDialog* dialog, - Scene_facegraph_item* poly_item) + relaxation_dialog(QDialog* dialog) { Ui::Tangential_relaxation_dialog ui; ui.setupUi(dialog); @@ -187,7 +186,7 @@ public Q_SLOTS: } // Create dialog box QDialog dialog(mw); - Ui::Tangential_relaxation_dialog ui = relaxation_dialog(&dialog, poly_item); + Ui::Tangential_relaxation_dialog ui = relaxation_dialog(&dialog); // Get values int i = dialog.exec(); @@ -223,8 +222,8 @@ public Q_SLOTS: pmesh, CGAL::Polygon_mesh_processing::parameters::number_of_iterations(nb_iter) .edge_is_constrained_map(selection_item->constrained_edges_pmap()) - .vertex_is_constrained_map(selection_item->constrained_vertices_pmap())); - + .vertex_is_constrained_map(selection_item->constrained_vertices_pmap()) + .relax_constraints(smooth_features)); selection_item->polyhedron_item()->invalidateOpenGLBuffers(); Q_EMIT selection_item->polyhedron_item()->itemChanged(); selection_item->invalidateOpenGLBuffers(); From 7ab2c079b34326df42e63882cc0e0346ad8555d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Wed, 23 Mar 2022 23:53:00 +0100 Subject: [PATCH 42/46] add an option to forbid some moves --- .../tangential_relaxation.h | 29 ++++++++++++++++++- .../test_mesh_smoothing.cpp | 16 ++++++++++ .../internal/parameters_interface.h | 1 + 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index d73ddc100b2..a690a13fe0b 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -29,6 +29,17 @@ namespace CGAL { namespace Polygon_mesh_processing { +namespace internal { +struct Allow_all_moves{ + template + constexpr inline bool operator()(vertex_descriptor, Point_3, Point_3) const + { + return true; + } +}; +} // internal namespace + + /*! * \ingroup PMP_meshing_grp * applies an iterative area-based tangential smoothing to the given range of vertices. @@ -97,6 +108,13 @@ namespace Polygon_mesh_processing { * \cgalParamDefault{`false`} * \cgalParamNEnd * +* \cgalParamNBegin{allow_move_functor} +* \cgalParamDescription{A function object used to determinate if a vertex move should be allowed or not} +* \cgalParamType{Unary functor that provides `bool operator()(vertex_descriptor v, Point_3 src, Point_3 tgt)` returning `true` +* if the vertex `v` can be move from `src` to `tgt`; %Point_3` being the value type of the vertex point map } +* \cgalParamDefault{If not provided, all moves are allowed.} +* \cgalParamNEnd +* * \cgalNamedParamsEnd * * \todo check if it should really be a triangle mesh or if a polygon mesh is fine @@ -176,6 +194,14 @@ void tangential_relaxation(const VertexRange& vertices, return true; }; + typedef typename internal_np::Lookup_named_param_def < + internal_np::allow_move_functor_t, + NamedParameters, + internal::Allow_all_moves// default + > ::type Shall_move; + Shall_move shall_move = choose_parameter(get_parameter(np, internal_np::allow_move_functor), + internal::Allow_all_moves()); + for (unsigned int nit = 0; nit < nb_iterations; ++nit) { #ifdef CGAL_PMP_TANGENTIAL_RELAXATION_VERBOSE @@ -267,7 +293,8 @@ void tangential_relaxation(const VertexRange& vertices, //check that no inversion happened double frac = 1.; while (frac > 0.03 //5 attempts maximum - && !check_normals(vp.first)) //if a face has been inverted + && ( !check_normals(vp.first) + || !shall_move(vp.first, initial_pos, get(vpm, vp.first)))) //if a face has been inverted { frac = 0.5 * frac; put(vpm, vp.first, initial_pos + frac * move);//shorten the move by 2 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 b84f0c57a2f..986bdca5b53 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 @@ -95,6 +95,22 @@ void test_tangential_relaxation(const std::string filename) .relax_constraints(false)); PMP::tangential_relaxation(vertices(mesh), mesh); PMP::tangential_relaxation(mesh); + + // test allow_move_functor + typename boost::property_map >::type vpm = + get(CGAL::dynamic_vertex_property_t(), mesh); + for (auto v : vertices(mesh)) + put(vpm, v, get(CGAL::vertex_point, mesh, v)); + auto no_move = [](typename boost::graph_traits::vertex_descriptor, Point, Point) + { + return false; + }; + PMP::tangential_relaxation(vertices(mesh), mesh, CGAL::parameters::allow_move_functor(no_move) + .vertex_point_map(vpm)); + for (auto v : vertices(mesh)) + { + assert(get(vpm, v) == get(CGAL::vertex_point, mesh, v)); + } } template diff --git a/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h b/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h index d597cf0b5d5..d9fe8962785 100644 --- a/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h +++ b/STL_Extension/include/CGAL/STL_Extension/internal/parameters_interface.h @@ -90,6 +90,7 @@ CGAL_add_named_parameter(overlap_test_t, overlap_test, do_overlap_test_of_bounde CGAL_add_named_parameter(preserve_genus_t, preserve_genus, preserve_genus) CGAL_add_named_parameter(apply_per_connected_component_t, apply_per_connected_component, apply_per_connected_component) CGAL_add_named_parameter(projection_functor_t, projection_functor, projection_functor) +CGAL_add_named_parameter(allow_move_functor_t, allow_move_functor, allow_move_functor) CGAL_add_named_parameter(throw_on_self_intersection_t, throw_on_self_intersection, throw_on_self_intersection) CGAL_add_named_parameter(clip_volume_t, clip_volume, clip_volume) CGAL_add_named_parameter(use_compact_clipper_t, use_compact_clipper, use_compact_clipper) From b56027ba4299f8ba9943c63a77be406739bccfdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 24 Mar 2022 09:07:00 +0100 Subject: [PATCH 43/46] typo --- .../CGAL/Polygon_mesh_processing/tangential_relaxation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index a690a13fe0b..4953d22cf3b 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -111,7 +111,7 @@ struct Allow_all_moves{ * \cgalParamNBegin{allow_move_functor} * \cgalParamDescription{A function object used to determinate if a vertex move should be allowed or not} * \cgalParamType{Unary functor that provides `bool operator()(vertex_descriptor v, Point_3 src, Point_3 tgt)` returning `true` -* if the vertex `v` can be move from `src` to `tgt`; %Point_3` being the value type of the vertex point map } +* if the vertex `v` can be moved from `src` to `tgt`; %Point_3` being the value type of the vertex point map } * \cgalParamDefault{If not provided, all moves are allowed.} * \cgalParamNEnd * From 8286dcbcb0049d4b2e293334bf45cf970a2f0689 Mon Sep 17 00:00:00 2001 From: Sebastien Loriot Date: Tue, 29 Mar 2022 08:24:22 +0200 Subject: [PATCH 44/46] Improve wording Co-authored-by: Mael --- Installation/CHANGES.md | 2 +- .../CGAL/Polygon_mesh_processing/tangential_relaxation.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index 6e345947bdc..83a6131413d 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -40,7 +40,7 @@ Release date: June 2022 ### [Polygon Mesh Processing](https://doc.cgal.org/5.5/Manual/packages.html#PkgPolygonMeshProcessing) - Added the function `CGAL::Polygon_mesh_processing::tangential_relaxation()`, which -applies an area-based tangential mesh smoothing to a surface triangle mesh vertices. +applies an area-based tangential mesh smoothing to the vertices of a surface triangle mesh. [Release 5.4](https://github.com/CGAL/cgal/releases/tag/v5.4) ----------- diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index 4953d22cf3b..056ef91ec66 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -51,7 +51,7 @@ struct Allow_all_moves{ * The descriptor types `boost::graph_traits::%face_descriptor` * and `boost::graph_traits::%halfedge_descriptor` must be * models of `Hashable`. -* @tparam VertexRange range of `boost::graph_traits::%face_descriptor`, +* @tparam VertexRange range of `boost::graph_traits::%vertex_descriptor`, * model of `Range`. Its iterator type is `ForwardIterator`. * @tparam NamedParameters a sequence of \ref bgl_namedparameters "Named Parameters" * @@ -78,7 +78,7 @@ struct Allow_all_moves{ * \cgalParamNEnd * * \cgalParamNBegin{number_of_iterations} -* \cgalParamDescription{the number of iterations smoothing iterations} +* \cgalParamDescription{the number of smoothing iterations} * \cgalParamType{unsigned int} * \cgalParamDefault{`1`} * \cgalParamNEnd From f4327cb28913bb18263c2452b5e6cb1cccb0ba00 Mon Sep 17 00:00:00 2001 From: Sebastien Loriot Date: Tue, 29 Mar 2022 15:09:56 +0200 Subject: [PATCH 45/46] use helper function --- .../CGAL/Polygon_mesh_processing/tangential_relaxation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index 056ef91ec66..d8568a8e39a 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -220,7 +220,7 @@ void tangential_relaxation(const VertexRange& vertices, // at each vertex, compute barycenter of neighbors for(vertex_descriptor v : vertices) { - if (get(vcm, v) || halfedge(v, tm)==boost::graph_traits::null_halfedge()) + if (get(vcm, v) || internal::is_isolated(v, tm)) continue; // collect hedges to detect if we have to handle boundary cases From f6f158a1219a904b07d0d39b3a48ad3439efe002 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 29 Mar 2022 15:15:54 +0200 Subject: [PATCH 46/46] fix confusing namespace --- .../CGAL/Polygon_mesh_processing/tangential_relaxation.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h index d8568a8e39a..f4462e75057 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/tangential_relaxation.h @@ -220,7 +220,7 @@ void tangential_relaxation(const VertexRange& vertices, // at each vertex, compute barycenter of neighbors for(vertex_descriptor v : vertices) { - if (get(vcm, v) || internal::is_isolated(v, tm)) + if (get(vcm, v) || CGAL::internal::is_isolated(v, tm)) continue; // collect hedges to detect if we have to handle boundary cases