diff --git a/BGL/include/CGAL/boost/graph/Seam_mesh.h b/BGL/include/CGAL/boost/graph/Seam_mesh.h index 4919c73b42c..c74accc8586 100644 --- a/BGL/include/CGAL/boost/graph/Seam_mesh.h +++ b/BGL/include/CGAL/boost/graph/Seam_mesh.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -32,6 +33,7 @@ namespace CGAL { #ifndef DOXYGEN_RUNNING + template class Seam_mesh_halfedge_descriptor { @@ -86,8 +88,92 @@ public: return 2 * hash_value(hd.tmhd) + static_cast(hd.seam); } }; + +template +class Seam_mesh_vertex_descriptor +{ +public: + Seam_mesh_halfedge_descriptor hd; + + Seam_mesh_vertex_descriptor() { } + + Seam_mesh_vertex_descriptor(const Seam_mesh_halfedge_descriptor& h) + : hd(h) + { } + + bool operator==(const Seam_mesh_vertex_descriptor& other) const + { + return (hd == other.hd); + } + + bool operator!=(const Seam_mesh_vertex_descriptor& other) const + { + return (hd != other.hd); + } + + bool operator<(const Seam_mesh_vertex_descriptor& other) const + { + return hd < other.hd; + } + + operator HD() const + { + return hd; + } + +#ifdef CGAL_SEAM_MESH_INSERT_OPERATOR + friend std::ostream& operator<<(std::ostream& os, const Seam_mesh_vertex_descriptor vd) + { + os << "seam mesh vertex: " << vd.hd; + return os; + } #endif + friend std::size_t hash_value(const Seam_mesh_vertex_descriptor& vd) + { + return hash_value(vd.hd.tmhd); + } +}; + +template +class Seam_mesh_edge_descriptor +{ +public: + Seam_mesh_halfedge_descriptor hd; + const SM* mesh_; + + Seam_mesh_edge_descriptor() : mesh_(nullptr) { } + + Seam_mesh_edge_descriptor(const Seam_mesh_halfedge_descriptor& hd, const SM* m) + : hd(hd), mesh_(m) + {} + + friend bool operator==(Seam_mesh_edge_descriptor e1, Seam_mesh_edge_descriptor e2) + { + return (e1.hd == e2.hd) || (e1.hd == e2.mesh_->opposite(e2.hd)); + } + + friend bool operator!=(Seam_mesh_edge_descriptor e1, Seam_mesh_edge_descriptor e2) + { + return ! (e1 == e2); + } + +#ifdef CGAL_SEAM_MESH_INSERT_OPERATOR + friend std::ostream& operator<<(std::ostream& os, const Seam_mesh_edge_descriptor& ed) + { + os << ed.hd; + return os; + } +#endif + + friend std::size_t hash_value(const Seam_mesh_edge_descriptor& ed) + { + return hash_value((std::min)(ed.hd, ed.mesh_->opposite(ed.hd))); + } +}; + +#endif // DOXYGEN_RUNNING + /// \ingroup PkgBGLAdaptors /// /// This class is a data structure that takes a triangle mesh, further refered @@ -112,6 +198,10 @@ class Seam_mesh typedef Seam_mesh Self; public: + /// The underlying mesh type + typedef TM Triangle_mesh; + + // backward compatibility typedef TM TriangleMesh; public: @@ -187,23 +277,15 @@ public: class halfedge_descriptor { public: - TM_halfedge_descriptor tmhd; - bool seam; - /// %Default constructor - halfedge_descriptor() : tmhd(), seam(false) { } + halfedge_descriptor(); - halfedge_descriptor(TM_halfedge_descriptor tmhd, bool seam = false) - : tmhd(tmhd), seam(seam) - { } + /// Constructor from a halfedge of the underlying mesh + halfedge_descriptor(TM_halfedge_descriptor tmhd, bool seam = false); #ifdef CGAL_SEAM_MESH_INSERT_OPERATOR /// Print the halfedge and if it is on a seam. - friend std::ostream& operator<<(std::ostream& os, const halfedge_descriptor& hd) - { - os << hd.tmhd << ((hd.seam)?" on seam":""); - return os; - } + friend std::ostream& operator<<(std::ostream& os, const halfedge_descriptor& hd); #endif }; #else @@ -265,13 +347,13 @@ public: return halfedge_descriptor(*hd, seam); } }; -#endif +#endif // DOXYGEN_RUNNING +#ifdef DOXYGEN_RUNNING /// This class represents a vertex of the seam mesh. /// /// Implementation note: to properly duplicate vertices that are on seams, - /// a vertex_descriptor is in fact represented as a halfedge of the underlying - /// mesh. + /// a vertex_descriptor is in fact represented as a halfedge of the seam mesh. /// /// \cgalModels `Descriptor` /// \cgalModels `LessThanComparable` @@ -280,48 +362,20 @@ public: class vertex_descriptor { public: - halfedge_descriptor hd; - /// %Default constructor - vertex_descriptor() { } + vertex_descriptor(); - vertex_descriptor(const halfedge_descriptor& h) - : hd(h) - { } - - bool operator==(const vertex_descriptor& other) const - { - return (hd == other.hd); - } - - bool operator!=(const vertex_descriptor& other) const - { - return (hd != other.hd); - } - - bool operator<(const vertex_descriptor& other) const - { - return hd < other.hd; - } - - operator TM_halfedge_descriptor() const - { - return hd; - } + /// Constructor from a seam mesh halfedge + vertex_descriptor(const halfedge_descriptor& h); #ifdef CGAL_SEAM_MESH_INSERT_OPERATOR - friend std::ostream& operator<<(std::ostream& os, const vertex_descriptor vd) - { - os << "seam mesh vertex: " << vd.hd; - return os; - } + /// Print the seam mesh vertex. + friend std::ostream& operator<<(std::ostream& os, const vertex_descriptor vd); #endif - - friend std::size_t hash_value(const vertex_descriptor& vd) - { - return hash_value(vd.hd.tmhd); - } }; +#else + typedef Seam_mesh_vertex_descriptor vertex_descriptor; +#endif // iterator #ifndef DOXYGEN_RUNNING @@ -403,48 +457,24 @@ public: }; #endif +#ifdef DOXYGEN_RUNNING /// This class represents an edge of the seam mesh. /// /// \cgalModels `Descriptor` + /// \cgalModels `Hashable` /// class edge_descriptor { - public: - halfedge_descriptor hd; - const Self* mesh_; + /// %Default constructor + edge_descriptor(); #ifdef CGAL_SEAM_MESH_INSERT_OPERATOR - friend - std::ostream& operator<<(std::ostream& os, const edge_descriptor& ed) - { - os << ed.hd; - return os; - } + friend std::ostream& operator<<(std::ostream& os, const edge_descriptor& ed); #endif - - edge_descriptor() - : mesh_(nullptr) - {} - - edge_descriptor(const halfedge_descriptor& hd, const Self* m) - : hd(hd), mesh_(m) - {} - - friend bool operator==(edge_descriptor e1, edge_descriptor e2) - { - return (e1.hd == e2.hd) || (e1.hd == e2.mesh_->opposite(e2.hd)); - } - - friend bool operator!=(edge_descriptor e1, edge_descriptor e2) - { - return ! (e1 == e2); - } - - friend std::size_t hash_value(const edge_descriptor& ed) - { - return hash_value((std::min)(ed.hd, ed.mesh_->opposite(ed.hd))); - } }; +#else + typedef Seam_mesh_edge_descriptor edge_descriptor; +#endif #ifndef DOXYGEN_RUNNING // iterator @@ -1120,6 +1150,46 @@ public: } // namespace CGAL +#ifndef CGAL_CFG_NO_STD_HASH + +namespace std { + +template +struct hash > + : public CGAL::cpp98::unary_function, std::size_t> +{ + std::size_t operator()(const CGAL::Seam_mesh_vertex_descriptor& v) const + { + return hash_value(v); + } +}; + +template +struct hash > + : public CGAL::cpp98::unary_function, std::size_t> +{ + std::size_t operator()(const CGAL::Seam_mesh_halfedge_descriptor& h) const + { + return hash_value(h); + } +}; + +template +struct hash > + : public CGAL::cpp98::unary_function, std::size_t> +{ + std::size_t operator()(const CGAL::Seam_mesh_edge_descriptor& e) const + { + return hash_value(e); + } +}; + +// Seam_mesh::face_descriptor is equal to TM_face_descriptor so nothing to do + +} // namespace std + +#endif // CGAL_CFG_NO_STD_HASH + #include #endif //CGAL_SEAM_MESH_H diff --git a/BGL/include/CGAL/boost/graph/internal/initialized_index_maps_helpers.h b/BGL/include/CGAL/boost/graph/internal/initialized_index_maps_helpers.h index c735823a0fa..a1842b8148a 100644 --- a/BGL/include/CGAL/boost/graph/internal/initialized_index_maps_helpers.h +++ b/BGL/include/CGAL/boost/graph/internal/initialized_index_maps_helpers.h @@ -135,7 +135,7 @@ struct Index_map_initializer void operator()(const PropertyTag, IndexPropertyMap, const Graph&) { // The property map is not writable; should never be here. - CGAL_assertion_msg(false, "You are trying to initialize a non-writable property map"); + CGAL_assertion_msg(false, "Initialization of a non-writable property map is impossible"); } }; @@ -171,7 +171,7 @@ IndexMap get_initialized_index_map_const(const IndexMap index_map, CGAL_USE(g); CGAL_USE(p); - // If you are passing a pmap via NPs, it must be initialized + // If a pmap is passed via NPs, it must be initialized CGAL_assertion(is_index_map_valid(p, index_map, g)); return index_map; @@ -185,7 +185,7 @@ IndexMap get_initialized_index_map(const IndexMap index_map, CGAL_USE(g); CGAL_USE(p); - // If you are passing a pmap via NPs, it must be initialized + // If a pmap is passed via NPs, it must be initialized CGAL_assertion(is_index_map_valid(p, index_map, g)); return index_map; diff --git a/Documentation/doc/biblio/cgal_manual.bib b/Documentation/doc/biblio/cgal_manual.bib index 6dcd0e958c7..dd8d522bda4 100644 --- a/Documentation/doc/biblio/cgal_manual.bib +++ b/Documentation/doc/biblio/cgal_manual.bib @@ -1153,6 +1153,12 @@ Teillaud" URL = {https://hal.inria.fr/hal-01568002}, } +@inproceedings{cgal:j-lrsspp-19, + title={Learning to Reconstruct Symmetric Shapes using Planar Parameterization of 3D Surface}, + author={Jain, Hardik and Wollhaf, Manuel and Hellwich, Olaf}, + booktitle={Proceedings of the IEEE International Conference on Computer Vision Workshops}, + year={2019} +} @book{ cgal:j-csl-99 ,author = "Nicolai M. Josuttis" @@ -1964,6 +1970,14 @@ ABSTRACT = {We present the first complete, exact and efficient C++ implementatio update = "09.11 penarand" } +@inproceedings{cgal:ssgh-tmpm-01, + title={Texture mapping progressive meshes}, + author={Sander, Pedro V and Snyder, John and Gortler, Steven J and Hoppe, Hugues}, + booktitle={Proceedings of the 28th annual conference on Computer graphics and interactive techniques}, + pages={409--416}, + year={2001} +} + @inproceedings{ cgal:s-cgehd-98 ,author = "Jonathan R. Shewchuk" ,title = "A Condition Guaranteeing the Existence of Higher-Dimensional diff --git a/Installation/CHANGES.md b/Installation/CHANGES.md index dbbf8eb950b..08950b2a87b 100644 --- a/Installation/CHANGES.md +++ b/Installation/CHANGES.md @@ -77,7 +77,6 @@ Release date: December 2020 - Added the functions `set_recycle_garbage()` and `does_recycle_garbage()` to the class `Surface_mesh`. - ### [dD Geometry Kernel](https://doc.cgal.org/5.2/Manual/packages.html#PkgKernelD) - The kernels [`Epick_d`](https://doc.cgal.org/5.2/Kernel_d/structCGAL_1_1Epick__d.html) @@ -87,6 +86,10 @@ Release date: December 2020 to deal with weighted points. +### [Surface Mesh Parameterization](https://doc.cgal.org/5.2/Manual/packages.html#PkgSurfaceMeshParameterization) +- Added a new parameterization method, Iterative Authalic Parameterization. It is based on the work of Jain, Hardik, Manuel Wollhaf, and Olaf Hellwich, + "Learning to Reconstruct Symmetric Shapes using Planar Parameterization of 3D Surface." (IEEE International Conference on Computer Vision Workshops, 2019). + [Release 5.1](https://github.com/CGAL/cgal/releases/tag/v5.1) ----------- diff --git a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh/Parameterization_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh/Parameterization_plugin.cpp index eab69bce277..51ab7a482ca 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh/Parameterization_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh/Parameterization_plugin.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -342,6 +343,7 @@ public: QAction* actionDCP = new QAction ("Discrete Conformal Map", mw); QAction* actionLSC = new QAction("Least Square Conformal Map", mw); QAction* actionDAP = new QAction("Discrete Authalic", mw); + QAction* actionIAP = new QAction("Iterative Authalic", mw); QAction* actionARAP = new QAction("As Rigid As Possible", mw); QAction* actionOTE = new QAction("Orbifold Tutte Embedding", mw); QAction* actionBTP = new QAction("Tutte Barycentric", mw); @@ -349,6 +351,7 @@ public: actionDCP->setObjectName("actionDCP"); actionLSC->setObjectName("actionLSC"); actionDAP->setObjectName("actionDAP"); + actionIAP->setObjectName("actionIAP"); actionARAP->setObjectName("actionARAP"); actionOTE->setObjectName("actionOTE"); actionBTP->setObjectName("actionBTP"); @@ -356,6 +359,7 @@ public: _actions << actionARAP << actionBTP << actionDAP + << actionIAP << actionDCP << actionLSC << actionMVC @@ -413,6 +417,7 @@ public Q_SLOTS: void on_actionDCP_triggered(); void on_actionLSC_triggered(); void on_actionDAP_triggered(); + void on_actionIAP_triggered(); void on_actionARAP_triggered(); void on_actionOTE_triggered(); void on_actionBTP_triggered(); @@ -485,8 +490,8 @@ public Q_SLOTS: } protected: - enum Parameterization_method { PARAM_MVC, PARAM_DCP, PARAM_LSC, - PARAM_DAP, PARAM_ARAP, PARAM_OTE, PARAM_BTP}; + enum Parameterization_method { PARAM_MVC, PARAM_DCP, PARAM_LSC, PARAM_DAP, + PARAM_IAP, PARAM_ARAP, PARAM_OTE, PARAM_BTP}; void parameterize(Parameterization_method method); private: @@ -804,6 +809,15 @@ void Polyhedron_demo_parameterization_plugin::parameterize(const Parameterizatio status = SMP::parameterize(sMesh, Parameterizer(), bhd, uv_pm); break; } + case PARAM_IAP: + { + new_item_name = tr("%1 (parameterized (IAP))").arg(poly_item->name()); + std::cout << "Parameterize (IAP)..." << std::endl; + typedef SMP::Iterative_authalic_parameterizer_3 Parameterizer; + Parameterizer parameterizer; + status = parameterizer.parameterize(sMesh, bhd, uv_pm, 15 /*iterations*/); + break; + } case PARAM_ARAP: { new_item_name = tr("%1 (parameterized (ARAP))").arg(poly_item->name()); @@ -1014,6 +1028,12 @@ void Polyhedron_demo_parameterization_plugin::on_actionDAP_triggered() parameterize(PARAM_DAP); } +void Polyhedron_demo_parameterization_plugin::on_actionIAP_triggered() +{ + std::cerr << "IAP..."; + parameterize(PARAM_IAP); +} + void Polyhedron_demo_parameterization_plugin::on_actionARAP_triggered() { std::cerr << "ARAP..."; diff --git a/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/Concepts/Parameterizer_3.h b/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/Concepts/Parameterizer_3.h index 971466d3773..aa3a8ad55ee 100644 --- a/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/Concepts/Parameterizer_3.h +++ b/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/Concepts/Parameterizer_3.h @@ -19,11 +19,11 @@ Construction and destruction are undefined. \cgalHasModel `CGAL::Surface_mesh_parameterization::Discrete_conformal_map_parameterizer_3` \cgalHasModel `CGAL::Surface_mesh_parameterization::LSCM_parameterizer_3` \cgalHasModel `CGAL::Surface_mesh_parameterization::Mean_value_coordinates_parameterizer_3` -\cgalHasModel `CGAL::Surface_mesh_parameterization::Orbifold_Tutte_parameterizer_3` \cgalHasModel `CGAL::Surface_mesh_parameterization::Circular_border_parameterizer_3` \cgalHasModel `CGAL::Surface_mesh_parameterization::Square_border_parameterizer_3` \cgalHasModel `CGAL::Surface_mesh_parameterization::Two_vertices_parameterizer_3` +\sa `CGAL::Surface_mesh_parameterization::Orbifold_Tutte_parameterizer_3` */ class Parameterizer_3 diff --git a/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/Doxyfile.in b/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/Doxyfile.in index a75bb8163ce..5734f82fb6b 100644 --- a/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/Doxyfile.in +++ b/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/Doxyfile.in @@ -2,8 +2,10 @@ PROJECT_NAME = "CGAL ${CGAL_DOC_VERSION} - Planar Parameterization of Triangulated Surface Meshes" EXTRACT_ALL = false +HIDE_UNDOC_MEMBERS = true HIDE_UNDOC_CLASSES = true WARN_IF_UNDOCUMENTED = false + HTML_EXTRA_FILES = ${CGAL_PACKAGE_DOC_DIR}/fig/nefertiti.jpg \ ${CGAL_PACKAGE_DOC_DIR}/fig/ARAP_new.jpg \ ${CGAL_PACKAGE_DOC_DIR}/fig/orbifold_new.jpg \ diff --git a/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/PackageDescription.txt b/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/PackageDescription.txt index 02776fd1004..ff8fd5778d1 100644 --- a/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/PackageDescription.txt +++ b/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/PackageDescription.txt @@ -6,7 +6,7 @@ \cgalPkgDescriptionBegin{Triangulated Surface Mesh Parameterization,PkgSurfaceMeshParameterization} \cgalPkgPicture{bimbaDetail.png} \cgalPkgSummaryBegin -\cgalPkgAuthors{Laurent Saboret, Pierre Alliez, Bruno Lévy, Mael Rouxel-Labbé, and Andreas Fabri} +\cgalPkgAuthors{Laurent Saboret, Pierre Alliez, Bruno Lévy, Mael Rouxel-Labbé, Andreas Fabri, and Hardik Jain} \cgalPkgDesc{Parameterizing a surface amounts to finding a one-to-one mapping from a suitable domain to the surface. In this package, we focus on triangulated surfaces that are homeomorphic to a disk and on piecewise linear mappings into a planar domain. @@ -49,9 +49,10 @@ This \cgal package implements several parameterization methods: Conditionally guaranteed if all weights are positive and border is convex. - Floater Mean Value Coordinates \cgalCite{cgal:f-mvc-03} : One-to-one mapping is guaranteed for convex border. + - Iterative Authalic Parameterization \cgalCite{cgal:j-lrsspp-19}. - Free border: - As Rigid As Possible Parameterization \cgalCite{liu2008local} - - Least Squares Conformal Maps \cgalCite{cgal:lprm-lscm-02}. + - Least Squares Conformal Maps (LSCM) \cgalCite{cgal:lprm-lscm-02}. - Borderless: - Orbifold Tutte Embeddings \cgalCite{aigerman2015orbifold}. @@ -60,6 +61,7 @@ The following classes implement the methods listed above: - `CGAL::Surface_mesh_parameterization::Barycentric_mapping_parameterizer_3` - `CGAL::Surface_mesh_parameterization::Discrete_authalic_parameterizer_3` - `CGAL::Surface_mesh_parameterization::Discrete_conformal_map_parameterizer_3` +- `CGAL::Surface_mesh_parameterization::Iterative_authalic_parameterizer_3` - `CGAL::Surface_mesh_parameterization::LSCM_parameterizer_3` - `CGAL::Surface_mesh_parameterization::Mean_value_coordinates_parameterizer_3` - `CGAL::Surface_mesh_parameterization::Orbifold_Tutte_parameterizer_3` diff --git a/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/Surface_mesh_parameterization.txt b/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/Surface_mesh_parameterization.txt index 383e48fa9c4..3a876b5977a 100644 --- a/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/Surface_mesh_parameterization.txt +++ b/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/Surface_mesh_parameterization.txt @@ -7,13 +7,7 @@ namespace CGAL { \anchor chapsurface_mesh_parameterization \cgalAutoToc -\authors Laurent Saboret, Pierre Alliez, Bruno Lévy, Andreas Fabri, and -Mael Rouxel-Labbé - -\warning The API and structure of this package have greatly changed with CGAL 4.11. -Users who wish to use the former API must use a version prior to 4.11. -Section \ref Surface_mesh_parameterizationBasics gives a gentle introduction to -the new, much simpler, API. +\authors Laurent Saboret, Pierre Alliez, Bruno Lévy, Mael Rouxel-Labbé, Andreas Fabri and Hardik Jain \section Surface_mesh_parameterizationIntroduction Introduction @@ -218,7 +212,7 @@ convex combination condition. This algorithm amounts to solve one sparse linear system for each set of parameter coordinates, with a \#vertices x \#vertices sparse and symmetric positive definite matrix (if the border vertices are eliminated from the linear system). -A coefficient \f$ (i, j)\f$ of the matrix is set to 1 for an edge linking +A coefficient \f$ (i,j)\f$ of the matrix is set to 1 for an edge linking the vertex \f$ v_i\f$ to the vertex \f$ v_j\f$, to minus the degree of the vertex \f$ v_i\f$ for a diagonal element, and to 0 for any other matrix entry. Although a bijective mapping is guaranteed when the border is convex, @@ -299,6 +293,29 @@ for both systems) is asymmetric. Floater Mean Value Coordinates. Rightmost: parameter space. \cgalFigureCaptionEnd +\subsubsection Surface_mesh_parameterizationIterativeAuthalic Iterative Authalic Parameterization + +`Surface_mesh_parameterization::Iterative_authalic_parameterizer_3` + +The Iterative Authalic parameterization method has been introduced by +Jain et al. \cgalCite{cgal:j-lrsspp-19}. This parameterization +is a fixed border parameterization and is part of the authalic parameterization family, +meaning that it aims to minimize area distortion between the input surface mesh and the parameterized output. +More precisely, the approach used by this parameterizer is to iteratively redistribute +the \f$ L_2\f$ stretch - as defined by Sander et al. \cgalCite{cgal:ssgh-tmpm-01} - over the mesh. + +\cgalFigureAnchor{Surface_mesh_parameterizationfigiterativeauthalic} +
+ +
+\cgalFigureCaptionBegin{Surface_mesh_parameterizationfigiterativeauthalic} +Iterative Authalic Parameterization (the blue line depicts the cut graph). Rightmost: parameter space. +\cgalFigureCaptionEnd + +Although this is a fixed border parameterization method, it does not guarantee a one-to-one mapping. +However, it will in practice yield valid results with less distortion than other authalic +parameterization methods. + \subsubsection secBorderParameterizationsforFixedMethods Border Parameterizations for Fixed Methods Parameterization methods for borders are used as traits classes modifying the @@ -475,55 +492,43 @@ underlying (input) mesh. \subsection Surface_mesh_parameterizationParameterization Parameterization Methods and Guarantees -
    - -
  • Fixed boundaries - -
      - -
    • One-to-one mapping - -Tutte's theorem guarantees a one-to-one mapping provided that the weights are all positive -and the border is convex. -It is the case for Tutte Barycentric Mapping and Floater Mean Value Coordinates. -It is not always the case for Discrete Conformal Map (cotangents) and -Discrete Authalic parameterization. - -
    • Non-singularity of the matrix - -Geshorgin's theorem guarantees the convergence of the solver if the matrix is diagonal dominant. -This is the case with positive weights (Tutte Barycentric Mapping and Floater Mean Value Coordinates). - -
    - -
  • Free boundaries - -
      - -
    • One-to-one mapping - -No guarantee is provided by either LSCM or ARAP parameterizations (both global -overlaps and triangle flips can occur). - -
    • Non-singularity of the matrix - -For LSCM, the matrix of the system is the Gram matrix of a matrix with maximal rank, -and is therefore non-singular (Gram theorem). - -
    - -
  • Boundary-less - -
      - -
    • One-to-one mapping - -The Orbifold-Tutte embedding is guaranteed to exist and to be computable -via a sparse linear system. - -
    - -
+
    +
  • Fixed boundaries +
      +
    • One-to-one mapping
      + Tutte's theorem guarantees a one-to-one mapping provided that the weights are all positive + and the border is convex. + It is the case for Tutte Barycentric Mapping and Floater Mean Value Coordinates. + It is not always the case for Discrete Conformal Map (cotangents), Discrete Authalic, + and Iterative Authalic parameterizations. +
    • +
    • Non-singularity of the matrix
      + Geshorgin's theorem guarantees the convergence of the solver if the matrix is diagonal dominant. + This is the case with positive weights (Tutte Barycentric Mapping and Floater Mean Value Coordinates). +
    • +
    +
  • +
  • Free boundaries +
      +
    • One-to-one mapping
      + No guarantee is provided by either LSCM or ARAP parameterizations (both global overlaps + and triangle flips can occur). +
    • +
    • Non-singularity of the matrix
      + For LSCM, the matrix of the system is the Gram matrix of a matrix with maximal rank, + and is therefore non-singular (Gram theorem). +
    • +
    +
  • +
  • Boundary-less +
      +
    • One-to-one mapping
      + The Orbifold-Tutte embedding is guaranteed to exist and to be computable + via a sparse linear system. +
    • +
    +
  • +
\section Surface_mesh_parameterizationExtendingthe Implementation History @@ -539,6 +544,9 @@ and Yaron Lipman. Finally, the class `Seam_mesh` was introduced to handle virtual borders. The class `Seam_mesh` is also a model of a `FaceGraph`, and replaces a wrapper which had a more complicated API. +Iterative authalic parameterization was added in CGAL 5.2 by Mael Rouxel-Labbé, +based on a prototype developed by Hardik Jain. + */ } /* namespace CGAL */ diff --git a/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/examples.txt b/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/examples.txt index a548836cd4c..db7aa8e187c 100644 --- a/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/examples.txt +++ b/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/examples.txt @@ -5,4 +5,5 @@ \example Surface_mesh_parameterization/seam_Polyhedron_3.cpp \example Surface_mesh_parameterization/simple_parameterization.cpp \example Surface_mesh_parameterization/square_border_parameterizer.cpp + \example Surface_mesh_parameterization/iterative_authalic_parameterizer.cpp */ diff --git a/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/fig/iterative_authalic.jpg b/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/fig/iterative_authalic.jpg new file mode 100644 index 00000000000..9bb4169df1f Binary files /dev/null and b/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/fig/iterative_authalic.jpg differ diff --git a/Surface_mesh_parameterization/examples/Surface_mesh_parameterization/CMakeLists.txt b/Surface_mesh_parameterization/examples/Surface_mesh_parameterization/CMakeLists.txt index eab97c84ecb..cfb737623d5 100644 --- a/Surface_mesh_parameterization/examples/Surface_mesh_parameterization/CMakeLists.txt +++ b/Surface_mesh_parameterization/examples/Surface_mesh_parameterization/CMakeLists.txt @@ -63,6 +63,8 @@ if ( CGAL_FOUND ) target_link_libraries(simple_parameterization PUBLIC CGAL::Eigen_support) create_single_source_cgal_program( "square_border_parameterizer.cpp" ) target_link_libraries(square_border_parameterizer PUBLIC CGAL::Eigen_support) + create_single_source_cgal_program( "iterative_authalic_parameterizer.cpp" ) + target_link_libraries(iterative_authalic_parameterizer PUBLIC CGAL::Eigen_support) if(SuiteSparse_FOUND) target_link_libraries(orbifold PRIVATE ${SuiteSparse_LIBRARIES}) diff --git a/Surface_mesh_parameterization/examples/Surface_mesh_parameterization/iterative_authalic_parameterizer.cpp b/Surface_mesh_parameterization/examples/Surface_mesh_parameterization/iterative_authalic_parameterizer.cpp new file mode 100644 index 00000000000..c74ed2bf50e --- /dev/null +++ b/Surface_mesh_parameterization/examples/Surface_mesh_parameterization/iterative_authalic_parameterizer.cpp @@ -0,0 +1,66 @@ +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +typedef CGAL::Simple_cartesian Kernel; +typedef Kernel::Point_2 Point_2; +typedef Kernel::Point_3 Point_3; +typedef CGAL::Surface_mesh Surface_mesh; + +typedef boost::graph_traits::vertex_descriptor vertex_descriptor; +typedef boost::graph_traits::halfedge_descriptor halfedge_descriptor; + +typedef CGAL::Unique_hash_map UV_uhm; +typedef boost::associative_property_map UV_pmap; + +namespace SMP = CGAL::Surface_mesh_parameterization; + +int main(int argc, char** argv) +{ + std::ifstream in((argc>1) ? argv[1] : "data/nefertiti.off"); + if(!in) + { + std::cerr << "Error: problem loading the input data" << std::endl; + return EXIT_FAILURE; + } + + Surface_mesh sm; + in >> sm; + + halfedge_descriptor bhd = CGAL::Polygon_mesh_processing::longest_border(sm).first; + + // The 2D points of the uv parametrisation will be written into this map + UV_uhm uv_uhm; + UV_pmap uv_map(uv_uhm); + + typedef SMP::Circular_border_arc_length_parameterizer_3 Border_parameterizer; + Border_parameterizer border_parameterizer; // the border parameterizer will automatically compute the corner vertices + + typedef SMP::Iterative_authalic_parameterizer_3 Parameterizer; + Parameterizer parameterizer(border_parameterizer); + + const unsigned int iterations = (argc > 2) ? std::atoi(argv[2]) : 15; + SMP::Error_code err = parameterizer.parameterize(sm, bhd, uv_map, iterations); + + if(err != SMP::OK) + { + std::cerr << "Error: " << SMP::get_error_message(err) << std::endl; + return EXIT_FAILURE; + } + + std::ofstream out("iterative_result.off"); + SMP::IO::output_uvmap_to_off(sm, bhd, uv_map, out); + + return EXIT_SUCCESS; +} diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/ARAP_parameterizer_3.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/ARAP_parameterizer_3.h index 2a638624662..45a297e4bf3 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/ARAP_parameterizer_3.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/ARAP_parameterizer_3.h @@ -129,7 +129,7 @@ namespace Surface_mesh_parameterization { /// a random vertex is pinned. /// /// If flips are present in the initial parameterization, a post-processing step -/// is applied using `CGAL::Surface_mesh_parameterization::MVC_post_processor_3` +/// is applied using `CGAL::Surface_mesh_parameterization::MVC_post_processor_3` /// to attempt to obtain a valid final embedding. /// /// A one-to-one mapping is *not* guaranteed. @@ -162,6 +162,7 @@ namespace Surface_mesh_parameterization { /// \endcode /// /// \sa `CGAL::Surface_mesh_parameterization::Fixed_border_parameterizer_3` +/// \sa `CGAL::Iterative_authalic_parameterizer_3` /// template < class TriangleMesh_, class BorderParameterizer_ = Default, @@ -194,28 +195,39 @@ public: #endif >::type Solver_traits; #else + /// Border parameterizer type typedef Border_parameterizer_ Border_parameterizer; + + /// Solver traits type typedef SolverTraits_ Solver_traits; + + /// Number type, deduced from the internal vertex point map of `Triangle_mesh` + typedef unspecified_type NT; #endif + /// Triangle mesh type + typedef TriangleMesh_ Triangle_mesh; + typedef TriangleMesh_ TriangleMesh; + /// Mesh halfedge type + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + // Private types private: - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - typedef typename boost::graph_traits::face_descriptor face_descriptor; - typedef typename boost::graph_traits::face_iterator face_iterator; - typedef typename boost::graph_traits::vertex_iterator vertex_iterator; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + typedef typename boost::graph_traits::face_iterator face_iterator; + typedef typename boost::graph_traits::vertex_iterator vertex_iterator; - typedef CGAL::Halfedge_around_target_circulator halfedge_around_target_circulator; - typedef CGAL::Halfedge_around_face_circulator halfedge_around_face_circulator; + typedef CGAL::Halfedge_around_target_circulator halfedge_around_target_circulator; + typedef CGAL::Halfedge_around_face_circulator halfedge_around_face_circulator; typedef boost::unordered_set Vertex_set; typedef std::vector Faces_vector; // Traits subtypes: - typedef typename internal::Kernel_traits::Kernel Kernel; + typedef typename internal::Kernel_traits::Kernel Kernel; typedef typename Kernel::FT NT; typedef typename Kernel::Point_2 Point_2; typedef typename Kernel::Point_3 Point_3; @@ -277,7 +289,7 @@ private: template void output_uvmap(const std::string filename, - const TriangleMesh& mesh, + const Triangle_mesh& mesh, const Vertex_set& vertices, const Faces_vector& faces, const VertexUVMap uvmap, @@ -292,7 +304,7 @@ private: typename VertexIndexMap> void output_uvmap(const std::string filename, const unsigned int iter, - const TriangleMesh& mesh, + const Triangle_mesh& mesh, const Vertex_set& vertices, const Faces_vector& faces, const VertexUVMap uvmap, @@ -331,12 +343,12 @@ private: // Private operations private: // Store the vertices and faces of the mesh in memory. - Error_code initialize_containers(const TriangleMesh& mesh, + Error_code initialize_containers(const Triangle_mesh& mesh, halfedge_descriptor bhd, Vertex_set& vertices, Faces_vector& faces) const { - internal::Containers_filler fc(mesh, vertices, &faces); + internal::Containers_filler fc(mesh, vertices, &faces); Polygon_mesh_processing::connected_component( face(opposite(bhd, mesh), mesh), mesh, @@ -351,7 +363,7 @@ private: // Initialize the UV values with a first parameterization of the input. template - Error_code compute_initial_uv_map(TriangleMesh& mesh, + Error_code compute_initial_uv_map(Triangle_mesh& mesh, halfedge_descriptor bhd, VertexUVMap uvmap, VertexIndexMap vimap) const @@ -372,11 +384,11 @@ private: // According to the paper, MVC is better for single border and LSCM is better // when there are multiple borders if(number_of_borders == 1) { - typedef Mean_value_coordinates_parameterizer_3 MVC_parameterizer; + typedef Mean_value_coordinates_parameterizer_3 MVC_parameterizer; MVC_parameterizer mvc_parameterizer; status = mvc_parameterizer.parameterize(mesh, bhd, uvmap, vimap, vpmap); } else { - typedef LSCM_parameterizer_3 LSCM_parameterizer; + typedef LSCM_parameterizer_3 LSCM_parameterizer; LSCM_parameterizer lscm_parameterizer; status = lscm_parameterizer.parameterize(mesh, bhd, uvmap, vimap, vpmap); } @@ -389,7 +401,7 @@ private: template - Error_code parameterize_border(const TriangleMesh& mesh, + Error_code parameterize_border(const Triangle_mesh& mesh, const Vertex_set& vertices, halfedge_descriptor bhd, VertexIndexMap vimap, @@ -422,14 +434,14 @@ private: } // Compute the cotangent of the angle between the vectors ij and ik. - void compute_cotangent_angle(const TriangleMesh& mesh, + void compute_cotangent_angle(const Triangle_mesh& mesh, halfedge_descriptor hd, vertex_descriptor vi, vertex_descriptor vj, vertex_descriptor vk, Cot_map ctmap) const { - typedef typename boost::property_map::const_type PPmap; const PPmap ppmap = get(vertex_point, mesh); @@ -442,7 +454,7 @@ private: } // Fill the map 'ctmap' with the cotangents of the angles of the faces of 'mesh'. - Error_code compute_cotangent_angles(const TriangleMesh& mesh, + Error_code compute_cotangent_angles(const Triangle_mesh& mesh, const Faces_vector& faces, Cot_map ctmap) const { @@ -468,8 +480,8 @@ private: return OK; } - // Compute w_ij = (i, j) coefficient of matrix A for j neighbor vertex of i. - NT compute_w_ij(const TriangleMesh& mesh, + // computes `w_ij`, the `(i,j)`-coefficient of matrix `A` for `j` neighbor vertex of `i`. + NT compute_w_ij(const Triangle_mesh& mesh, halfedge_descriptor hd, const Cot_map ctmap) const { @@ -496,7 +508,7 @@ private: // \pre Line i of A must contain only zeros. template Error_code fill_linear_system_matrix(Matrix& A, - const TriangleMesh& mesh, + const Triangle_mesh& mesh, vertex_descriptor vertex, const Cot_map ct_map, VertexIndexMap vimap) const @@ -538,7 +550,7 @@ private: // after (at least two) border vertices parameterization. template - Error_code initialize_matrix_A(const TriangleMesh& mesh, + Error_code initialize_matrix_A(const Triangle_mesh& mesh, const Vertex_set& vertices, const Cot_map ctmap, VertexIndexMap vimap, @@ -695,7 +707,7 @@ private: // Compute the root that gives the lowest face energy. template - std::size_t compute_root_with_lowest_energy(const TriangleMesh& mesh, + std::size_t compute_root_with_lowest_energy(const Triangle_mesh& mesh, face_descriptor fd, const Cot_map ctmap, const Local_points& lp, @@ -722,7 +734,7 @@ private: // Compute the root that gives the lowest face energy. template - std::size_t compute_root_with_lowest_energy(const TriangleMesh& mesh, + std::size_t compute_root_with_lowest_energy(const Triangle_mesh& mesh, face_descriptor fd, const Cot_map ctmap, const Local_points& lp, @@ -748,7 +760,7 @@ private: // Compute the optimal values of the linear transformation matrices Lt. template - Error_code compute_optimal_Lt_matrices(const TriangleMesh& mesh, + Error_code compute_optimal_Lt_matrices(const Triangle_mesh& mesh, const Faces_vector& faces, const Cot_map ctmap, const Local_points& lp, @@ -874,13 +886,13 @@ private: } // Compute the local parameterization (2D) of a face and store it in memory. - void project_face(const TriangleMesh& mesh, + void project_face(const Triangle_mesh& mesh, vertex_descriptor vi, vertex_descriptor vj, vertex_descriptor vk, Local_points& lp) const { - typedef typename boost::property_map::const_type PPmap; const PPmap ppmap = get(vertex_point, mesh); @@ -900,7 +912,7 @@ private: // Utility for fill_linear_system_rhs(): // Compute the local isometric parameterization (2D) of the faces of the mesh. - Error_code compute_local_parameterization(const TriangleMesh& mesh, + Error_code compute_local_parameterization(const Triangle_mesh& mesh, const Faces_vector& faces, Local_points& lp, Lp_map lpmap) const @@ -936,9 +948,9 @@ private: return OK; } - // Compute the coefficient b_ij = (i, j) of the right hand side vector B, + // Compute the coefficient b_ij = (i,j) of the right hand side vector B, // for j neighbor vertex of i. - void compute_b_ij(const TriangleMesh& mesh, + void compute_b_ij(const Triangle_mesh& mesh, halfedge_descriptor hd, const Cot_map ctmap, const Local_points& lp, @@ -1009,7 +1021,7 @@ private: // \pre Vertex i musn't be already parameterized. // \pre Lines i of Bu and Bv must be zero. template - Error_code fill_linear_system_rhs(const TriangleMesh& mesh, + Error_code fill_linear_system_rhs(const Triangle_mesh& mesh, vertex_descriptor vertex, const Cot_map ctmap, const Local_points& lp, @@ -1053,7 +1065,7 @@ private: template - Error_code compute_rhs(const TriangleMesh& mesh, + Error_code compute_rhs(const Triangle_mesh& mesh, const Vertex_set& vertices, const Cot_map ctmap, const Local_points& lp, @@ -1091,7 +1103,7 @@ private: template - Error_code update_solution(const TriangleMesh& mesh, + Error_code update_solution(const Triangle_mesh& mesh, const Vertex_set& vertices, const Cot_map ctmap, const Local_points& lp, @@ -1147,7 +1159,7 @@ private: // Compute the current energy of a face, given a linear transformation matrix. template - NT compute_current_face_energy(const TriangleMesh& mesh, + NT compute_current_face_energy(const Triangle_mesh& mesh, face_descriptor fd, const Cot_map ctmap, const Local_points& lp, @@ -1190,7 +1202,7 @@ private: // Compute the current energy of a face. template - NT compute_current_face_energy(const TriangleMesh& mesh, + NT compute_current_face_energy(const Triangle_mesh& mesh, face_descriptor fd, const Cot_map ctmap, const Local_points& lp, @@ -1207,7 +1219,7 @@ private: // Compute the current energy of the parameterization. template - NT compute_current_energy(const TriangleMesh& mesh, + NT compute_current_energy(const Triangle_mesh& mesh, const Faces_vector& faces, const Cot_map ctmap, const Local_points& lp, @@ -1232,14 +1244,14 @@ private: // the (hopefully few) flips in the result. template - Error_code post_process(const TriangleMesh& mesh, + Error_code post_process(const Triangle_mesh& mesh, const Vertex_set& vertices, const Faces_vector& faces, halfedge_descriptor bhd, VertexUVMap uvmap, const VertexIndexMap vimap) const { - typedef MVC_post_processor_3 Post_processor; + typedef MVC_post_processor_3 Post_processor; Post_processor p; Error_code status = p.parameterize(mesh, vertices, faces, bhd, uvmap, vimap); @@ -1255,28 +1267,28 @@ private: // Public operations public: - /// Check if the 3D -> 2D mapping is one-to-one. - template - bool is_one_to_one_mapping(const TriangleMesh& mesh, - const Faces_vector& faces, + /// returns whether the 3D -> 2D mapping is one-to-one. + template + bool is_one_to_one_mapping(const Triangle_mesh& mesh, + const FaceRange& faces, const VertexUVMap uvmap) const { return internal::is_one_to_one_mapping(mesh, faces, uvmap); } - /// Compute a mapping from a triangular 3D surface mesh to a piece of the 2D space. + /// computes a mapping from a triangular 3D surface mesh to a piece of the 2D space. /// The mapping is piecewise linear (linear in each triangle). /// The result is the (u,v) pair image of each vertex of the 3D surface. /// /// \tparam VertexUVmap must be a model of `ReadWritePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and - /// %Point_2 (type deduced from `TriangleMesh` using `Kernel_traits`) + /// `boost::graph_traits::%vertex_descriptor` as key type and + /// %Point_2 (type deduced from `Triangle_mesh` using `Kernel_traits`) /// as value type. /// \tparam VertexIndexMap must be a model of `ReadablePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and + /// `boost::graph_traits::%vertex_descriptor` as key type and /// a unique integer as value type. /// \tparam VertexParameterizedMap must be a model of `ReadWritePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and + /// `boost::graph_traits::%vertex_descriptor` as key type and /// a Boolean as value type. /// /// \param mesh a triangulated surface. @@ -1291,12 +1303,16 @@ public: template - Error_code parameterize(TriangleMesh& mesh, + Error_code parameterize(Triangle_mesh& mesh, halfedge_descriptor bhd, VertexUVMap uvmap, VertexIndexMap vimap, VertexParameterizedMap vpmap) { + CGAL_precondition(is_valid_polygon_mesh(mesh)); + CGAL_precondition(is_triangle_mesh(mesh)); + CGAL_precondition(bhd != boost::graph_traits::null_halfedge() && is_border(bhd, mesh)); + Error_code status = OK; // vertices and faces containers diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Barycentric_mapping_parameterizer_3.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Barycentric_mapping_parameterizer_3.h index fa8307ab6ce..c53c4c56d20 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Barycentric_mapping_parameterizer_3.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Barycentric_mapping_parameterizer_3.h @@ -21,6 +21,7 @@ #include #include +#include #if defined(CGAL_EIGEN3_ENABLED) #include @@ -43,8 +44,8 @@ namespace Surface_mesh_parameterization { /// This class is a strategy called by the main /// parameterization algorithm `Fixed_border_parameterizer_3::parameterize()` and it: /// - provides the template parameters `BorderParameterizer_` and `SolverTraits_`. -/// - implements compute_w_ij() to compute `w_ij = (i,j)`, coefficient of -/// the matrix A for `j` neighbor vertex of `i`, based on Tutte Barycentric +/// - implements compute_w_ij() to compute `w_ij`, the `(i,j)`-coefficient of +/// the matrix `A` for `j` neighbor vertex of `i`, based on Tutte Barycentric /// Mapping method. /// /// \cgalModels `Parameterizer_3` @@ -108,24 +109,32 @@ public: #endif >::type Solver_traits; #else + /// Border parameterizer type typedef Border_parameterizer_ Border_parameterizer; - typedef SolverTraits_ SolverTraits; + + /// Solver traits type + typedef SolverTraits_ Solver_traits; #endif + /// Triangle mesh type + typedef TriangleMesh_ Triangle_mesh; + typedef TriangleMesh_ TriangleMesh; + /// Mesh vertex type + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + + /// Mesh halfedge type + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + private: // Superclass - typedef Fixed_border_parameterizer_3 Base; // Private types private: - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - typedef CGAL::Vertex_around_target_circulator vertex_around_target_circulator; - typedef typename Base::NT NT; // Solver traits subtypes: @@ -139,24 +148,24 @@ public: ///< %Object that maps the surface's border to 2D space. Solver_traits sparse_la = Solver_traits()) ///< Traits object to access a sparse linear system. - : Fixed_border_parameterizer_3(border_param, sparse_la) { } // Default copy constructor and operator =() are fine - /// Check if the 3D -> 2D mapping is one-to-one. + /// returns whether the 3D -> 2D mapping is one-to-one. template - bool is_one_to_one_mapping(const TriangleMesh& mesh, + bool is_one_to_one_mapping(const Triangle_mesh& mesh, halfedge_descriptor bhd, const VertexUVMap uvmap) const { - /// Theorem: A one-to-one mapping is guaranteed if all w_ij coefficients - /// are > 0 (for j vertex neighbor of i) and if the surface + /// Theorem: A one-to-one mapping is guaranteed if all `w_ij` coefficients + /// are > 0 (for `j` vertex neighbor of `i`) and if the surface /// border is mapped onto a 2D convex polygon. - /// Here, all w_ij coefficients = 1 (for j vertex neighbor of i), thus a + /// Here, all `w_ij` coefficients are equal to `1` (for `j` vertex neighbor of `i`), thus a /// valid embedding is guaranteed if the surface border is mapped /// onto a 2D convex polygon. return (Base::get_border_parameterizer().is_border_convex() || @@ -165,13 +174,12 @@ public: // Protected operations protected: - /// Compute w_ij = (i,j), coefficient of matrix A for j neighbor vertex of i. - virtual NT compute_w_ij(const TriangleMesh& /* mesh */, + /// computes `w_ij`, the coefficient of matrix `A` for `j` neighbor vertex of `i`. + virtual NT compute_w_ij(const Triangle_mesh& /* mesh */, vertex_descriptor /* main_vertex_v_i */, - vertex_around_target_circulator /* neighbor_vertex_v_j */ ) const + Vertex_around_target_circulator /* neighbor_vertex_v_j */ ) const { - /// In the Tutte Barycentric Mapping algorithm, we have w_ij = 1, - /// for j neighbor vertex of i. + /// In the Tutte Barycentric Mapping algorithm, we have `w_ij = 1`, for `j` neighbor vertex of `i`. return 1.; } }; diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Circular_border_parameterizer_3.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Circular_border_parameterizer_3.h index 48c275b91ba..d7b10934389 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Circular_border_parameterizer_3.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Circular_border_parameterizer_3.h @@ -62,29 +62,33 @@ class Circular_border_parameterizer_3 { // Public types public: - typedef TriangleMesh_ TriangleMesh; + /// Triangle mesh type + typedef TriangleMesh_ Triangle_mesh; - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + /// Mesh vertex type + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + + /// Mesh halfedge type + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; // Protected types protected: - typedef typename internal::Kernel_traits::PPM PPM; - typedef typename internal::Kernel_traits::Kernel Kernel; + typedef typename internal::Kernel_traits::PPM PPM; + typedef typename internal::Kernel_traits::Kernel Kernel; typedef typename Kernel::FT NT; typedef typename Kernel::Point_2 Point_2; typedef typename Kernel::Vector_3 Vector_3; // Protected operations protected: - virtual NT compute_edge_length(const TriangleMesh& mesh, + virtual NT compute_edge_length(const Triangle_mesh& mesh, vertex_descriptor source, vertex_descriptor target) const = 0; // Private operations private: // Compute the total length of the border - NT compute_border_length(const TriangleMesh& mesh, + NT compute_border_length(const Triangle_mesh& mesh, halfedge_descriptor bhd) const { NT len = 0.0; @@ -96,21 +100,21 @@ private: // Public operations public: - /// Assign to the mesh's border vertices a 2D position (i.e.\ a (u,v) pair) + /// assigns to the mesh's border vertices a 2D position (i.e.\ a `(u,v)` pair) /// on the circle. Mark them as parameterized. /// /// The distribution of vertices over the circle depends on the function /// `compute_edge_length()`. /// /// \tparam VertexUVmap must be a model of `ReadWritePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and - /// %Point_2 (type deduced from `TriangleMesh` using `Kernel_traits`) + /// `boost::graph_traits::%vertex_descriptor` as key type and + /// %Point_2 (type deduced from `Triangle_mesh` using `Kernel_traits`) /// as value type. /// \tparam VertexIndexMap must be a model of `ReadablePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and + /// `boost::graph_traits::%vertex_descriptor` as key type and /// a unique integer as value type. /// \tparam VertexParameterizedMap must be a model of `ReadWritePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and + /// `boost::graph_traits::%vertex_descriptor` as key type and /// a Boolean as value type. /// /// \param mesh a triangulated surface. @@ -124,7 +128,7 @@ public: template - Error_code parameterize(const TriangleMesh& mesh, + Error_code parameterize(const Triangle_mesh& mesh, halfedge_descriptor bhd, VertexUVmap uvmap, VertexIndexMap /* vimap */, @@ -159,7 +163,7 @@ public: return OK; } - /// Indicate if border's shape is convex. + /// indicates if border's shape is convex. bool is_border_convex() const { return true; } virtual ~Circular_border_parameterizer_3() { } @@ -192,20 +196,18 @@ class Circular_border_uniform_parameterizer_3 // Public types public: // We have to repeat the types exported by superclass - /// @cond SKIP_IN_MANUAL - typedef TriangleMesh_ TriangleMesh; - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - /// @endcond + typedef TriangleMesh_ Triangle_mesh; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; // Private types private: - typedef Circular_border_parameterizer_3 Base; + typedef Circular_border_parameterizer_3 Base; typedef typename Base::NT NT; // Protected operations protected: - /// Compute the length of an edge. - virtual NT compute_edge_length(const TriangleMesh& /* mesh */, + /// computes the length of an edge. + virtual NT compute_edge_length(const Triangle_mesh& /* mesh */, vertex_descriptor /* source */, vertex_descriptor /* target */) const { @@ -243,24 +245,22 @@ class Circular_border_arc_length_parameterizer_3 // Public types public: // We have to repeat the types exported by superclass - /// @cond SKIP_IN_MANUAL - typedef TriangleMesh_ TriangleMesh; - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - /// @endcond + typedef TriangleMesh_ Triangle_mesh; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; // Private types private: - typedef Circular_border_parameterizer_3 Base; + typedef Circular_border_parameterizer_3 Base; - typedef typename Base::PPM PPM; - typedef typename Base::NT NT; - typedef typename Base::Vector_3 Vector_3; + typedef typename Base::PPM PPM; + typedef typename Base::NT NT; + typedef typename Base::Vector_3 Vector_3; // Protected operations protected: - /// Compute the length of an edge. - virtual NT compute_edge_length(const TriangleMesh& mesh, + /// computes the length of an edge. + virtual NT compute_edge_length(const Triangle_mesh& mesh, vertex_descriptor source, vertex_descriptor target) const { diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Discrete_authalic_parameterizer_3.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Discrete_authalic_parameterizer_3.h index c99c3d92d99..61ca95aab95 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Discrete_authalic_parameterizer_3.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Discrete_authalic_parameterizer_3.h @@ -48,8 +48,8 @@ namespace Surface_mesh_parameterization { /// This class is a strategy called by the main /// parameterization algorithm `Fixed_border_parameterizer_3::parameterize()` and it: /// - provides the template parameters `BorderParameterizer_` and `SolverTraits_`. -/// - implements `compute_w_ij()` to compute w_ij = (i, j), coefficient of the matrix A -/// for j neighbor vertex of i, based on Discrete Authalic Parameterization algorithm. +/// - implements `compute_w_ij()` to compute `w_ij`, the `(i,j)`-coefficient of the matrix `A` +/// for `j` neighbor vertex of `i`, based on Discrete Authalic Parameterization algorithm. /// /// \cgalModels `Parameterizer_3` /// @@ -75,6 +75,8 @@ namespace Surface_mesh_parameterization { /// \endcode /// /// \sa `CGAL::Surface_mesh_parameterization::Fixed_border_parameterizer_3` +/// \sa `CGAL::Surface_mesh_parameterization::ARAP_parameterizer_3` +/// \sa `CGAL::Surface_mesh_parameterization::Iterative_authalic_parameterizer_3` /// template < class TriangleMesh_, class BorderParameterizer_ = Default, @@ -112,23 +114,31 @@ public: #endif >::type Solver_traits; #else + /// Border parameterizer type typedef Border_parameterizer_ Border_parameterizer; + + /// Solver traits type typedef SolverTraits_ Solver_traits; #endif + /// Triangle mesh type + typedef TriangleMesh_ Triangle_mesh; + typedef TriangleMesh_ TriangleMesh; + /// Mesh vertex type + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + // Private types private: // Superclass - typedef Fixed_border_parameterizer_3 Base; // Private types private: - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef CGAL::Vertex_around_target_circulator vertex_around_target_circulator; + typedef CGAL::Vertex_around_target_circulator vertex_around_target_circulator; // Traits subtypes: typedef typename Base::PPM PPM; @@ -148,7 +158,7 @@ public: ///< %Object that maps the surface's border to 2D space. Solver_traits sparse_la = Solver_traits()) ///< Traits object to access a sparse linear system. - : Fixed_border_parameterizer_3(border_param, sparse_la) { } @@ -157,14 +167,14 @@ public: // Protected operations protected: - /// Compute w_ij = (i, j), coefficient of matrix A for j neighbor vertex of i. + /// computes `w_ij`, the (i,j), coefficient of matrix `A` for `j` neighbor vertex of `i`. /// /// \param mesh a triangulated surface. /// \param main_vertex_v_i the vertex of `mesh` with index `i` /// \param neighbor_vertex_v_j the vertex of `mesh` with index `j` - virtual NT compute_w_ij(const TriangleMesh& mesh, + virtual NT compute_w_ij(const Triangle_mesh& mesh, vertex_descriptor main_vertex_v_i, - vertex_around_target_circulator neighbor_vertex_v_j) const + Vertex_around_target_circulator neighbor_vertex_v_j) const { const PPM ppmap = get(vertex_point, mesh); diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Discrete_conformal_map_parameterizer_3.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Discrete_conformal_map_parameterizer_3.h index 2eda6baaaf4..62bf4f29b3c 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Discrete_conformal_map_parameterizer_3.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Discrete_conformal_map_parameterizer_3.h @@ -48,8 +48,8 @@ namespace Surface_mesh_parameterization { /// This class is a strategy called by the main /// parameterization algorithm `Fixed_border_parameterizer_3::parameterize()` and it: /// - provides the template parameters `BorderParameterizer_` and `SolverTraits_`. -/// - implements `compute_w_ij()` to compute w_ij = (i, j), coefficient of matrix A -/// for j neighbor vertex of i, based on Discrete Conformal Map method. +/// - implements `compute_w_ij()` to compute `w_ij`, the `(i,j)`-coefficient of matrix `A`, +/// for `j` neighbor vertex of `i`, based on Discrete Conformal Map method. /// /// \cgalModels `Parameterizer_3` /// @@ -112,23 +112,29 @@ public: #endif >::type Solver_traits; #else + /// Border parameterizer type typedef Border_parameterizer_ Border_parameterizer; + + /// Solver traits type typedef SolverTraits_ Solver_traits; #endif + /// Triangle mesh type + typedef TriangleMesh_ Triangle_mesh; + typedef TriangleMesh_ TriangleMesh; // Private types private: // Superclass - typedef Fixed_border_parameterizer_3 Base; // Private types private: - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef CGAL::Vertex_around_target_circulator vertex_around_target_circulator; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef CGAL::Vertex_around_target_circulator vertex_around_target_circulator; // Traits subtypes: typedef typename Base::Kernel Kernel; @@ -148,7 +154,7 @@ public: ///< %Object that maps the surface's border to 2D space. Solver_traits sparse_la = Solver_traits()) ///< Traits object to access a sparse linear system. - : Fixed_border_parameterizer_3(border_param, sparse_la) { } @@ -157,14 +163,14 @@ public: // Protected operations protected: - /// Compute w_ij = (i,j) coefficient of matrix A for j neighbor vertex of i. + /// computes `w_ij`, the `(i,j)`-coefficient of matrix `A`, for `j` neighbor vertex of `i`. /// /// \param mesh a triangulated surface. /// \param main_vertex_v_i the vertex of `mesh` with index `i` /// \param neighbor_vertex_v_j the vertex of `mesh` with index `j` - virtual NT compute_w_ij(const TriangleMesh& mesh, + virtual NT compute_w_ij(const Triangle_mesh& mesh, vertex_descriptor main_vertex_v_i, - vertex_around_target_circulator neighbor_vertex_v_j) const // its target is main_vertex_v_i + Vertex_around_target_circulator neighbor_vertex_v_j) const // its target is main_vertex_v_i { const PPM ppmap = get(vertex_point, mesh); diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Fixed_border_parameterizer_3.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Fixed_border_parameterizer_3.h index 99de2c63519..1a4590353a3 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Fixed_border_parameterizer_3.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Fixed_border_parameterizer_3.h @@ -58,8 +58,8 @@ namespace Surface_mesh_parameterization { /// Nevertheless, it implements most of the parameterization algorithm `parameterize()`. /// Subclasses are *Strategies* \cgalCite{cgal:ghjv-dpero-95} that modify the behavior of this algorithm: /// - They provide the template parameters `BorderParameterizer_` and `SolverTraits_`. -/// - They implement `compute_w_ij()` to compute w_ij = (i, j), coefficient of matrix A -/// for j neighbor vertex of i. +/// - They implement `compute_w_ij()` to compute `w_ij`, the `(i,j)`-coefficient of matrix `A` +/// for `j` neighbor vertex of `i`. /// // @todo `Fixed_border_parameterizer_3` should remove border vertices // from the linear systems in order to have a symmetric positive definite @@ -88,7 +88,6 @@ namespace Surface_mesh_parameterization { /// Eigen::IncompleteLUT< double > > > /// \endcode /// -/// \sa `CGAL::Surface_mesh_parameterization::ARAP_parameterizer_3` /// \sa `CGAL::Surface_mesh_parameterization::Barycentric_mapping_parameterizer_3` /// \sa `CGAL::Surface_mesh_parameterization::Discrete_authalic_parameterizer_3` /// \sa `CGAL::Surface_mesh_parameterization::Discrete_conformal_map_parameterizer_3` @@ -121,34 +120,44 @@ public: #endif >::type Solver_traits; #else + /// Border parameterizer type typedef Border_parameterizer_ Border_parameterizer; + + /// Solver traits type typedef SolverTraits_ Solver_traits; #endif + /// Triangle mesh type + typedef TriangleMesh_ Triangle_mesh; + typedef TriangleMesh_ TriangleMesh; + /// Mesh vertex type + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + + /// Mesh halfedge type + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + + /// Solver vector type + typedef typename Solver_traits::Vector Vector; + + /// Solver matrix type + typedef typename Solver_traits::Matrix Matrix; + // Private types private: - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - - typedef CGAL::Vertex_around_target_circulator vertex_around_target_circulator; - typedef CGAL::Vertex_around_face_circulator vertex_around_face_circulator; + typedef CGAL::Vertex_around_target_circulator vertex_around_target_circulator; // Protected types protected: // Traits subtypes: - typedef typename internal::Kernel_traits::Kernel Kernel; - typedef typename internal::Kernel_traits::PPM PPM; + typedef typename internal::Kernel_traits::Kernel Kernel; + typedef typename internal::Kernel_traits::PPM PPM; typedef typename Kernel::FT NT; typedef typename Kernel::Point_2 Point_2; typedef typename Kernel::Point_3 Point_3; typedef typename Kernel::Vector_3 Vector_3; - // Solver traits subtypes: - typedef typename Solver_traits::Vector Vector; - typedef typename Solver_traits::Matrix Matrix; - // Public operations public: /// Constructor @@ -164,20 +173,20 @@ public: // Default copy constructor and operator =() are fine - /// Compute a one-to-one mapping from a triangular 3D surface mesh + /// computes a one-to-one mapping from a triangular 3D surface mesh /// to a piece of the 2D space. /// The mapping is piecewise linear (linear in each triangle). - /// The result is the (u,v) pair image of each vertex of the 3D surface. + /// The result is the `(u,v)` pair image of each vertex of the 3D surface. /// /// \tparam VertexUVmap must be a model of `ReadWritePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and - /// %Point_2 (type deduced from `TriangleMesh` using `Kernel_traits`) + /// `boost::graph_traits::%vertex_descriptor` as key type and + /// %Point_2 (type deduced from `Triangle_mesh` using `Kernel_traits`) /// as value type. /// \tparam VertexIndexMap must be a model of `ReadablePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and + /// `boost::graph_traits::%vertex_descriptor` as key type and /// a unique integer as value type. /// \tparam VertexParameterizedMap must be a model of `ReadWritePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and + /// `boost::graph_traits::%vertex_descriptor` as key type and /// a Boolean as value type. /// /// \param mesh a triangulated surface. @@ -193,18 +202,22 @@ public: template - Error_code parameterize(TriangleMesh& mesh, + Error_code parameterize(Triangle_mesh& mesh, halfedge_descriptor bhd, VertexUVmap uvmap, VertexIndexMap vimap, VertexParameterizedMap vpmap) { + CGAL_precondition(is_valid_polygon_mesh(mesh)); + CGAL_precondition(is_triangle_mesh(mesh)); + CGAL_precondition(bhd != boost::graph_traits::null_halfedge() && is_border(bhd, mesh)); + Error_code status = OK; typedef boost::unordered_set Vertex_set; Vertex_set vertices; - internal::Containers_filler fc(mesh, vertices); + internal::Containers_filler fc(mesh, vertices); Polygon_mesh_processing::connected_component( face(opposite(bhd, mesh), mesh), mesh, @@ -294,16 +307,16 @@ public: // Protected operations protected: - /// Initialize A, Bu and Bv after border parameterization. + /// initializes `A`, `Bu` and `Bv` after border parameterization. /// Fill the border vertices' lines in both linear systems: /// "u = constant" and "v = constant". /// /// \tparam VertexUVmap must be a model of `ReadWritePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and - /// %Point_2 (type deduced from `TriangleMesh` using `Kernel_traits`) + /// `boost::graph_traits::%vertex_descriptor` as key type and + /// %Point_2 (type deduced from `Triangle_mesh` using `Kernel_traits`) /// as value type. /// \tparam VertexIndexMap must be a model of `ReadablePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and + /// `boost::graph_traits::%vertex_descriptor` as key type and /// a unique integer as value type. /// /// \param A the matrix in both linear system @@ -315,11 +328,11 @@ protected: /// \param vimap an instanciation of the class `VertexIndexMap`. /// /// \pre Vertices must be indexed (`vimap` must be initialized). - /// \pre A, Bu and Bv must be allocated. + /// \pre `A`, `Bu`, and `Bv` must be allocated. /// \pre Border vertices must be parameterized. template void initialize_system_from_mesh_border(Matrix& A, Vector& Bu, Vector& Bv, - const TriangleMesh& mesh, + const Triangle_mesh& mesh, halfedge_descriptor bhd, VertexUVmap uvmap, VertexIndexMap vimap) const @@ -337,16 +350,15 @@ protected: } } - /// Compute w_ij, coefficient of matrix A for j neighbor vertex of i. + /// computes `w_ij`, coefficient of matrix `A` for `j` neighbor vertex of `i`. /// Implementation note: Subclasses must at least implement compute_w_ij(). /// /// \param mesh a triangulated surface. /// \param main_vertex_v_i the vertex of `mesh` with index `i` /// \param neighbor_vertex_v_j the vertex of `mesh` with index `j` - virtual NT compute_w_ij(const TriangleMesh& mesh, + virtual NT compute_w_ij(const Triangle_mesh& mesh, vertex_descriptor main_vertex_v_i, - vertex_around_target_circulator neighbor_vertex_v_j) const - = 0; + Vertex_around_target_circulator neighbor_vertex_v_j) const = 0; /// Compute the line i of matrix A for i inner vertex: /// - call compute_w_ij() to compute the A coefficient w_ij for each neighbor v_j. @@ -361,7 +373,7 @@ protected: Error_code setup_inner_vertex_relations(Matrix& A, Vector&, Vector&, - const TriangleMesh& mesh, + const Triangle_mesh& mesh, vertex_descriptor vertex, VertexIndexMap vimap) const { diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Iterative_authalic_parameterizer_3.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Iterative_authalic_parameterizer_3.h new file mode 100644 index 00000000000..66369441365 --- /dev/null +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Iterative_authalic_parameterizer_3.h @@ -0,0 +1,1168 @@ +// Copyright (c) 2020 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é + +// This file has been adopted from CGAL, to integrate the below mentioned paper +// +// Paper : Learning to Reconstruct Symmetric Shapes using Planar Parameterization of 3D Surface +// Author(s) : Hardik Jain, Manuel Wöllhaf, Olaf Hellwich +// Conference : IEEE International Conference on Computer Vision Workshops (ICCVW) 2019 +// + +#ifndef CGAL_SURFACE_MESH_PARAMETERIZATION_ITERATIVE_AUTHALIC_PARAMETERIZER_3_H +#define CGAL_SURFACE_MESH_PARAMETERIZATION_ITERATIVE_AUTHALIC_PARAMETERIZER_3_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#if defined(CGAL_EIGEN3_ENABLED) +#include +#endif + +#include + +#include +#include +#include +#include +#include + +#define DEBUG_L0 1 // @fixme + +/// \file Iterative_authalic_parameterizer_3.h + +namespace CGAL { +namespace Surface_mesh_parameterization { + +/// \ingroup PkgSurfaceMeshParameterizationMethods +/// +/// The class `Iterative_authalic_parameterizer_3` implements the *Iterative Parameterization* algorithm, +/// as described by Jain et al. \cgalCite{cgal:j-lrsspp-19}. +/// +/// This parameterization is a fixed border parameterization and is part of the authalic +/// parameterization family, meaning that it aims to minimize area distortion +/// between the input surface mesh and the parameterized output. +/// More precisely, the approach used by this parameterizer is to iteratively redistribute +/// the \f$ L_2\f$ stretch - as defined by Sander et al. \cgalCite{cgal:ssgh-tmpm-01} - over the mesh. +/// +/// \tparam TriangleMesh_ must be a model of `FaceGraph`. +/// \tparam BorderParameterizer_ is a Strategy to parameterize the surface border +/// and must be a model of `Parameterizer_3`.
+/// %Default: +/// \code +/// Circular_border_arc_length_parameterizer_3 +/// \endcode +/// +/// \tparam SolverTraits_ must be a model of `SparseLinearAlgebraTraits_d`.
+/// Note that the system is *not* symmetric because border vertices are not removed from the system.
+/// %Default: If \ref thirdpartyEigen "Eigen" 3.1 (or greater) is available +/// and `CGAL_EIGEN3_ENABLED` is defined, then an overload of `Eigen_solver_traits` +/// is provided as default parameter: +/// \code +/// CGAL::Eigen_solver_traits< +/// Eigen::BiCGSTAB::EigenType, +/// Eigen::IncompleteLUT< double > > > +/// \endcode +/// +/// \sa `CGAL::Surface_mesh_parameterization::Discrete_authalic_parameterizer_3` +/// \sa `CGAL::Surface_mesh_parameterization::ARAP_parameterizer_3` +/// +template +class Iterative_authalic_parameterizer_3 +{ +public: +#ifndef DOXYGEN_RUNNING + typedef typename Default::Get >::type Border_parameterizer; + + typedef typename Default::Get::EigenType, + Eigen::IncompleteLUT > > +#else + SolverTraits_ // no parameter provided, and Eigen is not enabled: so don't compile! +#endif + >::type Solver_traits; +#else + /// Border parameterizer type + typedef Border_parameterizer_ Border_parameterizer; + + /// Solver traits type + typedef SolverTraits_ Solver_traits; +#endif + + /// Triangle mesh type + typedef TriangleMesh_ Triangle_mesh; + + typedef TriangleMesh_ TriangleMesh; + + // Private types +private: + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + typedef CGAL::Vertex_around_target_circulator vertex_around_target_circulator; + typedef CGAL::Face_around_target_circulator face_around_target_circulator; + + typedef typename internal::Kernel_traits::Kernel Kernel; + + typedef typename Kernel::FT NT; + typedef typename Kernel::Point_2 Point_2; + typedef typename Kernel::Point_3 Point_3; + typedef typename Kernel::Vector_3 Vector_3; + + typedef typename internal::Kernel_traits::PPM PPM; + typedef typename boost::property_traits::reference PPM_ref; + + typedef std::unordered_set Vertex_set; + + // Solver traits subtypes: + typedef typename Solver_traits::Vector Vector; + typedef typename Solver_traits::Matrix Matrix; + + typedef CGAL::dynamic_vertex_property_t Vertex_double_tag; + typedef typename boost::property_map::type Vertex_Double_map; + typedef CGAL::dynamic_vertex_property_t Vertex_bool_tag; + typedef typename boost::property_map::type Vertex_bool_map; + typedef CGAL::dynamic_vertex_property_t Vertex_int_tag; + typedef typename boost::property_map::type Vertex_int_map; + typedef CGAL::dynamic_vertex_property_t Vertex_point2_tag; + typedef typename boost::property_map::type Vertex_point2_map; + typedef CGAL::dynamic_face_property_t Face_NT_tag; + typedef typename boost::property_map::type Face_NT_map; + + // Fields +private: + // Object that maps the surface's border onto a 2D space. + Border_parameterizer m_border_parameterizer; + + // Traits object to solve a sparse linear system + Solver_traits m_linear_algebra; + + // Object that keeps the last best UV Map + Vertex_point2_map m_last_best_uv_map; + + // Counter to keep track of failure of Linear solver + int m_linear_solver_failures; + + Vertex_Double_map m_vertex_L2_map; + Face_NT_map m_face_L2_map; + Face_NT_map m_face_areas; + + // Protected accessors +protected: + /// Get the object that maps the surface's border onto a 2D space. + Border_parameterizer& get_border_parameterizer() { return m_border_parameterizer; } + + /// Get the sparse linear algebra (traits object to access the linear system). + Solver_traits& get_linear_algebra_traits() { return m_linear_algebra; } + + // Public operations +public: + /// Constructor + /// + /// \param border_parameterizer %Object that maps the surface's border to 2D space + /// \param sparse_la Traits object to access a sparse linear system + /// + Iterative_authalic_parameterizer_3(Border_parameterizer border_parameterizer = Border_parameterizer(), + Solver_traits sparse_la = Solver_traits()) + : + m_border_parameterizer(border_parameterizer), + m_linear_algebra(sparse_la) + { } + + // Disable copy constructor and operator =() due to property maps + Iterative_authalic_parameterizer_3(const Iterative_authalic_parameterizer_3&) = delete; + Iterative_authalic_parameterizer_3& operator=(const Iterative_authalic_parameterizer_3&) = delete; + + // Distortion functions +public: + // Measure L2 stretch + template + NT compute_area_distortion(const FaceRange& face_range, + const NT A_3D, + Triangle_mesh& tmesh, + const VertexUVmap uvmap) + { + Face_NT_map area_2DMap = get(Face_NT_tag(), tmesh); + + std::vector area_dist; + NT A_2D = 0; + + for(face_descriptor f : face_range) + { + // get area in parameterised mesh + const halfedge_descriptor h = halfedge(f, tmesh); + const NT a_2D = abs(CGAL::area(get(uvmap, source(h, tmesh)), + get(uvmap, target(h, tmesh)), + get(uvmap, target(next(h, tmesh), tmesh)))); + put(area_2DMap, f, a_2D); + A_2D += a_2D; + } + + for(face_descriptor f : face_range) + { + const NT a_3D = get(m_face_areas, f); + const NT a_2D = get(area_2DMap, f); + + area_dist.push_back(abs(a_3D/A_3D - a_2D/A_2D)); + } + + return std::accumulate(area_dist.begin(), area_dist.end(), NT(0)); + } + + // IO Helpers +public: + template + void print_matrix(const Vertex_set& vertices, + const VertexIndexMap vimap, + const Matrix& A, + const std::string name) + { + std::cout << "Matrix " << name << "(" << A.row_dimension() << "x" << A.column_dimension() << ")" << std::endl; + + Matrix A1(A.row_dimension(), A.column_dimension()); + int r=0, c=0; + for(vertex_descriptor v1 : vertices) + { + int i = get(vimap, v1); + for(vertex_descriptor v2 : vertices) + { + int j = get(vimap, v2); + A1.set_coef(r, c, A.get_coef(i, j)); + ++c; + } + + ++r; + c = 0; + } + + for(int r=0; r + void print_vector(const Vertex_set& vertices, + const VertexIndexMap vimap, + const Vector& A, + const std::string name) + { + std::cout << "Vector " << name << "(" << A.size() << ")" << std::endl; + Vector A1(A.size()); + int r = 0; + for(vertex_descriptor v1 : vertices) + { + int i = get(vimap,v1); + A1.set(r, A(i)); + ++r; + } + + for(int r=0; r + void copy_sparse_matrix(const Matrix& src, + Matrix& dest, + const Triangle_mesh& tmesh, + const Vertex_set& vertices, + const VertexIndexMap vimap) + { + CGAL_precondition(src.row_dimension() == dest.row_dimension()); + CGAL_precondition(src.column_dimension() == dest.column_dimension()); + + for(vertex_descriptor vertex : vertices) + { + const int i = get(vimap, vertex); + vertex_around_target_circulator v_j(halfedge(vertex, tmesh), tmesh), end = v_j; + CGAL_For_all(v_j, end) + { + const int j = get(vimap, *v_j); + dest.set_coef(i, j, src.get_coef(i, j), false); + } + } + } + +private: + double compute_vertex_L2(const Triangle_mesh& tmesh, + const vertex_descriptor v) const + { + NT phi = 0, local_area = 0; + + for(face_descriptor f : CGAL::faces_around_target(halfedge(v, tmesh), tmesh)) + { + if(f == boost::graph_traits::null_face()) + continue; + + phi += CGAL::square(get(m_face_L2_map, f)) * get(m_face_areas, f); + local_area += get(m_face_areas, f); + } + + return sqrt(phi / local_area); + } + + void compute_vertices_L2(const Vertex_set& vertices, + Triangle_mesh& tmesh) + { + m_vertex_L2_map = get(Vertex_double_tag(), tmesh); + for(vertex_descriptor v : vertices) + { + put(m_vertex_L2_map, v, compute_vertex_L2(tmesh, v)); +// std::cout << "Vertex L2: " << v << " = " << compute_vertex_L2(tmesh, v) << std::endl; + } + } + + NT get_A(const std::array& uv_points) const + { + NT A = (((uv_points[1]->x() - uv_points[0]->x()) * (uv_points[2]->y() - uv_points[0]->y())) + - ((uv_points[2]->x() - uv_points[0]->x()) * (uv_points[1]->y() - uv_points[0]->y()))) / NT(2); + + CGAL_warning(A != NT(0)); // means degenerate face in the param space + if(A == NT(0)) + return NT(1); + + return A; + } + + Point_3 get_Ss(const std::array& mesh_points, + const std::array& uv_points, + const NT den) const + { + const NT dt0 = uv_points[1]->y() - uv_points[2]->y(); + const NT dt1 = uv_points[2]->y() - uv_points[0]->y(); + const NT dt2 = uv_points[0]->y() - uv_points[1]->y(); + Point_3 Ss(den * (mesh_points[0]->x()*dt0 + mesh_points[1]->x()*dt1 + mesh_points[2]->x()*dt2), + den * (mesh_points[0]->y()*dt0 + mesh_points[1]->y()*dt1 + mesh_points[2]->y()*dt2), + den * (mesh_points[0]->z()*dt0 + mesh_points[1]->z()*dt1 + mesh_points[2]->z()*dt2)); + return Ss; + } + + Point_3 get_St(const std::array& mesh_points, + const std::array& uv_points, + const NT den) const + { + const NT ds0 = uv_points[2]->x() - uv_points[1]->x(); + const NT ds1 = uv_points[0]->x() - uv_points[2]->x(); + const NT ds2 = uv_points[1]->x() - uv_points[0]->x(); + Point_3 St(den * (mesh_points[0]->x()*ds0 + mesh_points[1]->x()*ds1 +mesh_points[2]->x()*ds2), + den * (mesh_points[0]->y()*ds0 + mesh_points[1]->y()*ds1 +mesh_points[2]->y()*ds2), + den * (mesh_points[0]->z()*ds0 + mesh_points[1]->z()*ds1 +mesh_points[2]->z()*ds2)); + return St; + } + + NT compute_inner_product(const Point_3& pointA, const Point_3& pointB) const + { + return ((pointA.x())*(pointB.x()) + (pointA.y())*(pointB.y()) + (pointA.z())*(pointB.z())); + } + + template + NT compute_face_L2(const face_descriptor f, + const Triangle_mesh& tmesh, + const VertexUVMap uvmap, + const PPM ppmap) const + { + std::array uv_points; + std::array mesh_points; + + halfedge_descriptor h = halfedge(f, tmesh); + for(std::size_t i=0; i<3; ++i) + { + vertex_descriptor v = target(h, tmesh); + + // just to be safe in case of weird VPM returning temporaries + typename boost::property_traits::reference uvp = get(uvmap, v); + PPM_ref p = get(ppmap, v); + + uv_points[i] = &uvp; + mesh_points[i] = &p; + + h = next(h, tmesh); + } + + // Formula from Sander et al. 'Texture Mapping Progressive Meshes' + const NT A = get_A(uv_points); + const NT den = NT(1) / (NT(2) * A); + const Point_3 Ss = get_Ss(mesh_points, uv_points, den); + const Point_3 St = get_St(mesh_points, uv_points, den); + + const NT a = compute_inner_product(Ss, Ss); + const NT c = compute_inner_product(St, St); + + return sqrt((a+c) / NT(2)); + } + + template + void compute_faces_L2(const FaceRange& face_range, + Triangle_mesh& tmesh, + const VertexUVMap uvmap) + { + m_face_L2_map = get(Face_NT_tag(), tmesh); + const PPM ppmap = get(vertex_point, tmesh); + + for(face_descriptor f : face_range) + { + put(m_face_L2_map, f, compute_face_L2(f, tmesh, uvmap, ppmap)); +// std::cout << "Face L2: " << f << " = " << compute_face_L2(f, tmesh, uvmap, ppmap) << std::endl; + } + } + + template + NT initialize_faces_areas(const FaceRange& face_range, + Triangle_mesh& tmesh) + { + m_face_areas = get(Face_NT_tag(), tmesh); + NT total_area = 0; + + for(face_descriptor f : face_range) + { + const NT f_area = Polygon_mesh_processing::face_area(f, tmesh); + put(m_face_areas, f, f_area); + total_area += f_area; + } + + return total_area; + } + + struct Neighbor_list + { + vertex_descriptor vertex; + double angle; + double length; + Vector_3 vector; + Point_2 uv; + double weight; + }; + + NT determinant(Point_2& v0, Point_2& v1) const + { + return (v0.x() * v1.y() - v1.x() * v0.y()); + } + + void get_bary_coords(const Point_2& uv0, const Point_2& uv1, const Point_2& uv2, + NT& tau0, NT& tau1, NT& tau2) const + { + const NT det0 = determinant(uv1, uv2); + const NT det1 = determinant(uv2, uv0); + const NT det2 = determinant(uv0, uv1); + const NT det3 = CGAL::determinant(Vector_2(uv1.x()-uv0.x(), uv1.y()-uv0.y()), + Vector_2(uv2.x()-uv0.x(), uv2.y()-uv0.y())); + CGAL_assertion(det3 > NT(0)); + if(det3 <= NT(0)) + det3 = NT(1); + + tau0 = det0 / det3; + tau1 = det1 / det3; + tau2 = det2 / det3; + } + + double angle(Vector_3& v0, Vector_3& v1) const // @fixme use helper's? + { + return std::acos(v0*v1 / (CGAL::sqrt(v0*v0) * CGAL::sqrt(v1*v1))); + } + + template + Error_code setup_inner_vertex_relations(Matrix& A, + Matrix& A_prev, + Vector&, + Vector&, + const Triangle_mesh& tmesh, + vertex_descriptor v, + VertexIndexMap vimap) + { + const PPM ppmap = get(vertex_point, tmesh); + + // circulate over vertices around 'vertex' to compute w_ii and w_ijs + std::vector neighbor_list; + int neighborsCounter = 0; + double theta_sum = 0.; + + // create Neighbor_list vector with vertex and vector + vertex_around_target_circulator v_j(halfedge(v, tmesh), tmesh), end_v_j = v_j; + CGAL_For_all(v_j, end_v_j) + { + Neighbor_list NL; + NL.vertex = *v_j; + NL.vector = Vector_3(get(ppmap, v), tmesh.point(*v_j)); + NL.length = sqrt(NL.vector.squared_length()); + neighbor_list.push_back(NL); + ++neighborsCounter; + } + + if(neighborsCounter < 2) + return ERROR_NON_TRIANGULAR_MESH; + + if(neighborsCounter == 2 && is_border(v, tmesh)) + { + std::cout << "Encountered inner border with valency-2 vertex (" << v << "), " + << "initializing with Tutte weights, this can affect optimization" << std::endl; + + // Tutte weights + for(int k=0; k 0) + w_ij *= -1.0; + w_ii -= w_ij; + + // Get j index + const int j = get(vimap, neighbor_list[n].vertex); + + // Set w_ij in matrix + A.set_coef(i, j, w_ij, true /*new*/); + A_prev.set_coef(i, j, w_ij, true); + } + + // Set w_ii in matrix + A.set_coef(i,i, w_ii, true /*new*/); + return OK; + } + + /// computes `w_ij`, coefficient of matrix `A` for `j` neighbor vertex of `i`. + /// + /// \param mesh a triangulated surface. + /// \param main_vertex_v_i the vertex of `mesh` with index `i` + /// \param neighbor_vertex_v_j the vertex of `mesh` with index `j` + NT compute_w_ij(const Triangle_mesh& tmesh, + vertex_descriptor main_vertex_v_i, + Vertex_around_target_circulator neighbor_vertex_v_j) const + { + const PPM ppmap = get(vertex_point, tmesh); + + const PPM_ref position_v_i = get(ppmap, main_vertex_v_i); + const PPM_ref position_v_j = get(ppmap, *neighbor_vertex_v_j); + + // Compute the square norm of v_j -> v_i vector + Vector_3 edge = position_v_i - position_v_j; + NT square_len = edge*edge; + + // Compute cotangent of (v_k,v_j,v_i) corner (i.e. cotan of v_j corner) + // if v_k is the vertex before v_j when circulating around v_i + vertex_around_target_circulator previous_vertex_v_k = neighbor_vertex_v_j; + --previous_vertex_v_k; + const PPM_ref position_v_k = get(ppmap, *previous_vertex_v_k); +// NT cotg_psi_ij = internal::cotangent(position_v_k, position_v_j, position_v_i); + NT cotg_beta_ij = internal::cotangent(position_v_i, position_v_k, position_v_j); + + // Compute cotangent of (v_i,v_j,v_l) corner (i.e. cotan of v_j corner) + // if v_l is the vertex after v_j when circulating around v_i + vertex_around_target_circulator next_vertex_v_l = neighbor_vertex_v_j; + ++next_vertex_v_l; + + const Point_3 position_v_l = get(ppmap, *next_vertex_v_l); +// NT cotg_theta_ij = internal::cotangent(position_v_i, position_v_j, position_v_l); + NT cotg_alpha_ij = internal::cotangent(position_v_j, position_v_l, position_v_i); + + NT weight = 0; + CGAL_assertion(square_len > NT(0)); // two points are identical! + if(square_len != NT(0)) + weight = cotg_beta_ij + cotg_alpha_ij; + + return weight; + } + + template + Error_code setup_inner_vertex_relations_cotangent(Matrix& A, + Vector&, + Vector&, + const Triangle_mesh& tmesh, + vertex_descriptor v, + VertexIndexMap& vimap) + { + const int i = get(vimap, v); + + // circulate over vertices around 'vertex' to compute w_ii and w_ijs + NT w_ii = 0; + + bool use_uniform_weights = false; + const int neighborsCounter = degree(v, tmesh); + if(neighborsCounter < 2) + { + return ERROR_NON_TRIANGULAR_MESH; + } + else if(neighborsCounter == 2 && is_border(v, tmesh)) + { + use_uniform_weights = true; + std::cerr << "Encountered inner border with valency-2 vertex (" << get(vimap, v) << ") " + << "initializing with uniform weights, this can affect optimization" << std::endl; + } + + vertex_around_target_circulator vj(halfedge(v, tmesh), tmesh), end = vj; + CGAL_For_all(vj, end) + { + NT w_ij = NT(-1); + if(!use_uniform_weights) + w_ij *= compute_w_ij(tmesh, v, vj); + + // w_ii = - sum of w_ijs + w_ii -= w_ij; + + // Get j index + const int j = get(vimap, *vj); +// std::cout << "W[" << i << ", " << j << "] = " << w_ij << std::endl; + + // Set w_ij in matrix + A.set_coef(i, j, w_ij, true /*new*/); + } + + // Set w_ii in matrix + A.set_coef(i, i, w_ii, true /*new*/); + + return OK; + } + + // Initialize the UV values with a first parameterization of the input. + template + Error_code setup_inner_vertex_relations_MVC(Matrix& A, + Vector&, + Vector&, + Triangle_mesh& tmesh, + vertex_descriptor v, + VertexIndexMap& vimap) const + { + auto vpm = get_const_property_map(CGAL::vertex_point, tmesh); + CGAL::internal::Mean_value_weight compute_mvc(tmesh, vpm); + + const int i = get(vimap, v); + + // circulate over vertices around 'vertex' to compute w_ii and w_ijs + NT w_ii = 0; + int vertexIndex = 0; + + for(halfedge_descriptor h : CGAL::halfedges_around_target(v, tmesh)) + { + NT w_ij = NT(-1) * compute_mvc(h); + // w_ii = - sum of w_ijs + w_ii -= w_ij; + + // Get j index + int j = get(vimap, source(h, tmesh)); + + // Set w_ij in matrix + A.set_coef(i, j, w_ij, true /*new*/); + vertexIndex++; + } + + if (vertexIndex < 2) + return ERROR_NON_TRIANGULAR_MESH; + + // Set w_ii in matrix + A.set_coef(i,i, w_ii, true /*new*/); + return OK; + } + + template + Error_code setup_iter_inner_vertex_relations(Matrix& A, + Matrix& A_prev, + Vector&, + Vector&, + const Triangle_mesh& tmesh, + vertex_descriptor v, + VertexIndexMap vimap, + NT gamma) + { + const int i = get(vimap, v); + + // circulate over vertices around 'v' to compute w_ii and w_ij's + NT w_ii = 0; + int vi = 0; + + vertex_around_target_circulator v_j(halfedge(v, tmesh), tmesh), end = v_j; + CGAL_For_all(v_j, end) + { + // Get j index + const int j = get(vimap, *v_j); + + // NT w_ij = A_prev.get_coef(i, j) / compute_sig_ij(v, *v_j) / gamma; + NT w_ij = A_prev.get_coef(i, j) / pow(compute_sig_ij(v, *v_j, NT(1)), gamma); + + // w_ii = - sum of w_ij's + w_ii -= w_ij; + + // Set w_ij in matrix + A.set_coef(i, j, w_ij, false); + + ++vi; + } + + if(vi < 2) + return ERROR_NON_TRIANGULAR_MESH; + + // Set w_ii in matrix + A.set_coef(i,i, w_ii, true /*new*/); + return OK; + } + + NT compute_sig_ij(const vertex_descriptor v_i, + const vertex_descriptor v_j, + const NT gamma) + { + const NT sig = (pow(get(m_vertex_L2_map, v_i), gamma) + pow(get(m_vertex_L2_map, v_j), gamma)) / 2.; + CGAL_assertion(sig > NT(0)); + return sig; + } + + template + void store_new_best_uv_map(const Vertex_set& vertices, + VertexUVMap uvmap) + { + for(vertex_descriptor v : vertices) + put(m_last_best_uv_map, v, get(uvmap, v)); + } + +public: + /// initializes `A`, `Bu`, and `Bv` after border parameterization. + /// Fill the border vertices' lines in both linear systems: + /// "u = constant" and "v = constant". + /// + /// \tparam VertexUVmap must be a model of `ReadWritePropertyMap` with + /// `boost::graph_traits::%vertex_descriptor` as key type and + /// %Point_2 (type deduced from `Triangle_mesh` using `Kernel_traits`) + /// as value type. + /// \tparam VertexIndexMap must be a model of `ReadablePropertyMap` with + /// `boost::graph_traits::%vertex_descriptor` as key type and + /// a unique integer as value type. + /// + /// \param A the matrix in both linear system + /// \param Bu the right hand side vector in the linear system of x coordinates + /// \param Bv the right hand side vector in the linear system of y coordinates + /// \param tmesh a triangulated surface + /// \param bhd a halfedge descriptor on the boundary of `mesh` + /// \param uvmap an instanciation of the class `VertexUVmap` + /// \param vimap an instanciation of the class `VertexIndexMap` + /// + /// \pre Vertices must be indexed (`vimap` must be initialized). + /// \pre `A`, `Bu`, and `Bv` must be allocated. + /// \pre Border vertices must be parameterized. + template + void initialize_system_from_mesh_border(Matrix& A, Vector& Bu, Vector& Bv, + const Triangle_mesh& tmesh, + halfedge_descriptor bhd, + VertexUVmap uvmap, + VertexIndexMap vimap) const + { + for(halfedge_descriptor hd : halfedges_around_face(bhd, tmesh)) + { + // Get vertex index in sparse linear system + int index = get(vimap, target(hd, tmesh)); + + // Write a diagonal coefficient of A + A.set_coef(index, index, 1, true /*new*/); + + // get the halfedge uv + // Write constant in Bu and Bv + const Point_2& uv = get(uvmap, target(hd, tmesh)); + Bu[index] = uv.x(); + Bv[index] = uv.y(); + } + } + + template + Error_code parameterize(Triangle_mesh& tmesh, + halfedge_descriptor bhd, + VertexUVmap uvmap, + VertexIndexMap vimap, + VertexParameterizedMap vpmap, + unsigned int& iterations, + double& error) + { + CGAL_precondition(is_valid_polygon_mesh(tmesh)); + CGAL_precondition(is_triangle_mesh(tmesh)); + CGAL_precondition(is_border(bhd, tmesh)); + + Error_code status = OK; + + Vertex_set cc_vertices; + std::vector cc_faces; + cc_faces.reserve(num_faces(tmesh)); + + internal::Containers_filler fc(tmesh, cc_vertices, &cc_faces); + Polygon_mesh_processing::connected_component(face(opposite(bhd, tmesh), tmesh), tmesh, + boost::make_function_output_iterator(fc)); + + std::size_t nv = cc_vertices.size(); + if(nv == 0) + return ERROR_EMPTY_MESH; + + // Compute (u,v) for border vertices and mark them as "parameterized" + status = get_border_parameterizer().parameterize(tmesh, bhd, uvmap, vimap, vpmap); + if (status != OK) + return status; + + // Create two sparse linear systems "A*Xu = Bu" and "A*Xv = Bv" (one line/column per vertex) + Matrix A(nv, nv); + Matrix A_prev(nv, nv); + Vector Xu(nv), Xv(nv), Bu(nv), Bv(nv); + std::vector err(iterations); + + // Initialize A, Xu, Xv, Bu and Bv after border parameterization + // Fill the border vertices' lines in both linear systems: + // "u = constant" and "v = constant" + initialize_system_from_mesh_border(A, Bu, Bv, tmesh, bhd, uvmap, vimap); + + // Fill the matrix for the inner vertices v_i: compute A's coefficient + // w_ij for each neighbor j; then w_ii = - sum of w_ijs + std::unordered_set main_border; + for(vertex_descriptor v : vertices_around_face(bhd, tmesh)) + main_border.insert(v); // @todo use marks? + + m_last_best_uv_map = get(Vertex_point2_tag(), tmesh); + + NT area_3D = initialize_faces_areas(cc_faces, tmesh); + + if(DEBUG_L0) + std::cout << std::endl; + + unsigned int last_best_i = 0; + NT gamma = 1; // @todo what value should that be + bool is_changed = false; + + // iterate it with the new weights + unsigned int i = 0; + while(i < iterations) + { + if(DEBUG_L0) + std::cout << "Iteration " << i << ", gamma = " << gamma << std::flush; + + // update weights for inner vertices + for(vertex_descriptor v : cc_vertices) + { + // inner vertices only + if(main_border.count(v) == 0) + { + // Compute the line i of matrix A for i inner vertex + if(i == 0) + { +#if 0 + status = setup_inner_vertex_relations(A, A_prev, Bu, Bv, tmesh, v, vimap); +#elif 1 + status = setup_inner_vertex_relations_cotangent(A, Bu, Bv, tmesh, v, vimap); +#else + status = setup_inner_vertex_relations_MVC(A, Bu, Bv, tmesh, v, vimap); +#endif + + if(status != OK) + return status; + } + else + { + status = setup_iter_inner_vertex_relations(A, A_prev, Bu, Bv, tmesh, v, vimap, gamma); + if(status != OK) + return status; + } + } + } + + // solve linear equations + // Solve "A*Xu = Bu". On success, solution is (1/Du) * Xu. + // Solve "A*Xv = Bv". On success, solution is (1/Dv) * Xv. + NT Du = 0, Dv = 0; + if(!get_linear_algebra_traits().linear_solver(A, Bu, Xu, Du) || + !get_linear_algebra_traits().linear_solver(A, Bv, Xv, Dv)) + { + if(DEBUG_L0) + std::cout << " Linear solver failure #" << m_linear_solver_failures << std::endl; + + status = ERROR_CANNOT_SOLVE_LINEAR_SYSTEM; + } + else + { + m_linear_solver_failures = 0; + } + + if(status != OK) + { + if(m_linear_solver_failures < 4) + { + // modify the weights and re-try the linear solver + ++m_linear_solver_failures; + gamma /= 2; + continue; + } + else + { + status = OK; + break; + } + } + + // WARNING: this package does not support homogeneous coordinates! + CGAL_postcondition(Du == NT(1)); + CGAL_postcondition(Dv == NT(1)); + +// for(std::size_t i=0; i 100) // @fixme is that reasonnable + { + break; + } + else + { + if(!is_changed) + { + gamma /= 2; + is_changed = true; + } + } + + ++i; + std::cout << std::endl; + } + + // Check postconditions + if(status != OK) + return status; + + if(i == 0 && i != iterations) + { + // means that the computation terminated for the first iteration may be because system was unsolvable + return ERROR_CANNOT_SOLVE_LINEAR_SYSTEM; + } + + for(vertex_descriptor v : cc_vertices) + put(uvmap, v, get(m_last_best_uv_map, v)); + + iterations = last_best_i; + error = err[last_best_i]; + + return status; + } + + template + Error_code parameterize(Triangle_mesh& tmesh, + halfedge_descriptor bhd, + VertexUVmap uvmap, + VertexIndexMap vimap, + VertexParameterizedMap vpmap, + unsigned int& iterations) + { + double unused_error; + return parameterize(tmesh, bhd, uvmap, vimap, vpmap, iterations, unused_error); + } + + /// computes a one-to-one mapping from a triangular 3D surface mesh + /// to a piece of the 2D space. + /// The mapping is piecewise linear (linear in each triangle). + /// The result is the `(u,v)` pair image of each vertex of the 3D surface. + /// + /// \tparam VertexUVmap must be a model of `ReadWritePropertyMap` with + /// `boost::graph_traits::%vertex_descriptor` as key type and + /// %Point_2 (type deduced from `Triangle_mesh` using `Kernel_traits`) + /// as value type. + /// \tparam VertexIndexMap must be a model of `ReadablePropertyMap` with + /// `boost::graph_traits::%vertex_descriptor` as key type and + /// a unique integer as value type. + /// \tparam VertexParameterizedMap must be a model of `ReadWritePropertyMap` with + /// `boost::graph_traits::%vertex_descriptor` as key type and + /// a Boolean as value type. + /// + /// \param tmesh a triangulated surface + /// \param bhd a halfedge descriptor on the boundary of `mesh` + /// \param uvmap an instanciation of the class `VertexUVmap` + /// \param vimap an instanciation of the class `VertexIndexMap` + /// \param vpmap an instanciation of the class `VertexParameterizedMap` + /// \param iterations an integer number of iterations to run the parameterization + /// + /// \pre `tmesh` must be a triangular mesh. + /// \pre The mesh border must be mapped onto a convex polygon. + /// \pre The vertices must be indexed (`vimap` must be initialized). + /// + template + Error_code parameterize(Triangle_mesh& tmesh, + halfedge_descriptor bhd, + VertexUVmap uvmap, + VertexIndexMap vimap, + VertexParameterizedMap vpmap, + // the '&' below is important, otherwise the function just calls itself + const unsigned int& iterations = 15) + { + unsigned int iter = iterations; // need a non-const ref + return parameterize(tmesh, bhd, uvmap, vimap, vpmap, iter); + } + + template + Error_code parameterize(Triangle_mesh& tmesh, + halfedge_descriptor bhd, + VertexUVmap uvmap, + const unsigned int iterations = 15) + { + Vertex_int_map vimap = get(Vertex_int_tag(), tmesh); + internal::fill_index_map_of_cc(bhd, tmesh, vimap); + + Vertex_bool_map vpmap = get(Vertex_bool_tag(), tmesh); + + return parameterize(tmesh, bhd, uvmap, vimap, vpmap, iterations); + } + + template + Error_code parameterize(Triangle_mesh& tmesh, + VertexUVmap uvmap, + const unsigned int iterations = 15) + { + const halfedge_descriptor bhd = Polygon_mesh_processing::longest_border(tmesh).first; + + return parameterize(tmesh, bhd, uvmap, iterations); + } +}; + +} // namespace Surface_mesh_parameterization +} // namespace CGAL + +#endif // CGAL_SURFACE_MESH_PARAMETERIZATION_ITERATIVE_AUTHALIC_PARAMETERIZER_3_H diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/LSCM_parameterizer_3.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/LSCM_parameterizer_3.h index 2907278e588..4ad502312aa 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/LSCM_parameterizer_3.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/LSCM_parameterizer_3.h @@ -75,7 +75,7 @@ namespace Surface_mesh_parameterization { /// and `CGAL_EIGEN3_ENABLED` is defined, then an overload of `Eigen_solver_traits` /// is provided as default parameter: /// \code -/// CGAL::Eigen_solver_traits > > +/// CGAL::Eigen_solver_traits > > /// \endcode /// Otherwise, it uses CGAL's wrapping function to the OpenNL library: /// \code @@ -100,12 +100,7 @@ public: #if defined(CGAL_EIGEN3_ENABLED) // WARNING: at the moment, the choice of SolverTraits_ is completely // ignored (see LeastSquaresSolver typedef) and `OpenNL::LinearSolver` - // is used anyway. If Eigen solver traits were to be used again, there is a bug - // in the line below to be first fixed: - // `Eigen_sparse_symmetric_matrix::EigenType` is a NON SYMMETRIC - // Eigen sparse matrix, and thus SolverTraits_::Matrix will be a NON SYMMETRIC matrix - // and the whole symmetry aspect will be completely ignored. - // @fixme + // is always used... CGAL::Eigen_solver_traits< Eigen::SimplicialLDLT::EigenType> > #else @@ -113,24 +108,29 @@ public: #endif >::type Solver_traits; #else + /// The border parameterizer typedef Border_parameterizer_ Border_parameterizer; + + /// Solver traits type typedef SolverTraits_ Solver_traits; #endif + /// Triangle mesh type + typedef TriangleMesh_ Triangle_mesh; + typedef TriangleMesh_ TriangleMesh; + /// Mesh halfedge type + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + // Private types private: - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - typedef typename boost::graph_traits::face_descriptor face_descriptor; - - typedef typename boost::graph_traits::vertex_iterator vertex_iterator; - typedef typename boost::graph_traits::face_iterator face_iterator; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; // Traits subtypes: - typedef typename internal::Kernel_traits::PPM PPM; - typedef typename internal::Kernel_traits::Kernel Kernel; + typedef typename internal::Kernel_traits::PPM PPM; + typedef typename internal::Kernel_traits::Kernel Kernel; typedef typename Kernel::FT NT; typedef typename Kernel::Point_2 Point_2; typedef typename Kernel::Point_3 Point_3; @@ -155,29 +155,29 @@ public: // Default copy constructor and operator =() are fine - /// Check if the 3D -> 2D mapping is one-to-one. + /// returns whether the 3D -> 2D mapping is one-to-one. template - bool is_one_to_one_mapping(const TriangleMesh& mesh, + bool is_one_to_one_mapping(const Triangle_mesh& mesh, halfedge_descriptor bhd, const VertexUVMap uvmap) const { return internal::is_one_to_one_mapping(mesh, bhd, uvmap); } - /// Compute a one-to-one mapping from a triangular 3D surface mesh + /// computes a one-to-one mapping from a triangular 3D surface mesh /// to a piece of the 2D space. /// The mapping is piecewise linear (linear in each triangle). - /// The result is the (u,v) pair image of each vertex of the 3D surface. + /// The result is the `(u,v)` pair image of each vertex of the 3D surface. /// /// \tparam VertexUVmap must be a model of `ReadWritePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and - /// %Point_2 (type deduced from `TriangleMesh` using `Kernel_traits`) + /// `boost::graph_traits::%vertex_descriptor` as key type and + /// %Point_2 (type deduced from `Triangle_mesh` using `Kernel_traits`) /// as value type. /// \tparam VertexIndexMap must be a model of `ReadablePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and + /// `boost::graph_traits::%vertex_descriptor` as key type and /// a unique integer as value type. /// \tparam VertexParameterizedMap must be a model of `ReadWritePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and + /// `boost::graph_traits::%vertex_descriptor` as key type and /// a Boolean as value type. /// /// \param mesh a triangulated surface. @@ -190,17 +190,21 @@ public: /// \pre The vertices must be indexed (`vimap` must be initialized). /// template - Error_code parameterize(TriangleMesh& mesh, + Error_code parameterize(Triangle_mesh& mesh, halfedge_descriptor bhd, VertexUVmap uvmap, VertexIndexMap vimap, VertexParameterizedMap vpmap) { + CGAL_precondition(is_valid_polygon_mesh(mesh)); + CGAL_precondition(is_triangle_mesh(mesh)); + CGAL_precondition(bhd != boost::graph_traits::null_halfedge() && is_border(bhd, mesh)); + // Fill containers boost::unordered_set ccvertices; std::vector ccfaces; - internal::Containers_filler fc(mesh, ccvertices, &ccfaces); + internal::Containers_filler fc(mesh, ccvertices, &ccfaces); Polygon_mesh_processing::connected_component( face(opposite(bhd, mesh), mesh), mesh, @@ -340,7 +344,7 @@ private: // in presence of degenerate triangles template Error_code setup_triangle_relations(LeastSquaresSolver& solver, - const TriangleMesh& mesh, + const Triangle_mesh& mesh, face_descriptor facet, VertexIndexMap vimap) const { diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/MVC_post_processor_3.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/MVC_post_processor_3.h index 1aeca305850..7803a1980a4 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/MVC_post_processor_3.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/MVC_post_processor_3.h @@ -14,6 +14,7 @@ #include +#include #include #include @@ -93,29 +94,33 @@ public: #endif >::type Solver_traits; #else + /// Solver traits type typedef SolverTraits_ Solver_traits; #endif + /// Triangle mesh type + typedef TriangleMesh_ Triangle_mesh; + typedef TriangleMesh_ TriangleMesh; // Private types private: // This class - typedef MVC_post_processor_3 Self; + typedef MVC_post_processor_3 Self; // Private types private: - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - typedef typename boost::graph_traits::face_descriptor face_descriptor; - typedef typename boost::graph_traits::face_iterator face_iterator; - typedef typename boost::graph_traits::vertex_iterator vertex_iterator; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + typedef typename boost::graph_traits::face_iterator face_iterator; + typedef typename boost::graph_traits::vertex_iterator vertex_iterator; typedef boost::unordered_set Vertex_set; typedef std::vector Faces_vector; // Traits subtypes: - typedef typename internal::Kernel_traits::Kernel Kernel; + typedef typename internal::Kernel_traits::Kernel Kernel; typedef typename Kernel::FT NT; typedef typename Kernel::Point_2 Point_2; typedef typename Kernel::Vector_2 Vector_2; @@ -189,12 +194,12 @@ private: // Private operations private: // Store the vertices and faces of the mesh in memory. - void initialize_containers(const TriangleMesh& mesh, + void initialize_containers(const Triangle_mesh& mesh, halfedge_descriptor bhd, Vertex_set& vertices, Faces_vector& faces) const { - internal::Containers_filler fc(mesh, vertices, &faces); + internal::Containers_filler fc(mesh, vertices, &faces); CGAL::Polygon_mesh_processing::connected_component( face(opposite(bhd, mesh), mesh), mesh, @@ -203,7 +208,7 @@ private: // Checks whether the polygon's border is simple. template - bool is_polygon_simple(const TriangleMesh& mesh, + bool is_polygon_simple(const Triangle_mesh& mesh, halfedge_descriptor bhd, const VertexUVMap uvmap) const { @@ -273,7 +278,7 @@ private: // Triangulate the convex hull of the border of the parameterization. template - Error_code triangulate_convex_hull(const TriangleMesh& mesh, + Error_code triangulate_convex_hull(const Triangle_mesh& mesh, halfedge_descriptor bhd, const VertexUVMap uvmap, CT& ct) const @@ -506,7 +511,7 @@ private: template - void fill_linear_system_matrix_mvc_from_mesh_halfedge(const TriangleMesh& mesh, + void fill_linear_system_matrix_mvc_from_mesh_halfedge(const Triangle_mesh& mesh, halfedge_descriptor hd, const VertexUVMap uvmap, const VertexIndexMap vimap, @@ -539,7 +544,7 @@ private: template - void fill_linear_system_matrix_mvc_from_mesh_face(const TriangleMesh& mesh, + void fill_linear_system_matrix_mvc_from_mesh_face(const Triangle_mesh& mesh, face_descriptor fd, const VertexUVMap uvmap, const VertexIndexMap vimap, @@ -562,7 +567,7 @@ private: typename VertexIndexMap, typename VertexParameterizedMap> Error_code compute_mvc_matrix(const CT& ct, - const TriangleMesh& mesh, + const Triangle_mesh& mesh, const Faces_vector& faces, const VertexUVMap uvmap, const VertexIndexMap vimap, @@ -660,7 +665,7 @@ private: typename VertexUVMap, typename VertexIndexMap, typename VertexParameterizedMap> - Error_code parameterize_convex_hull_with_MVC(const TriangleMesh& mesh, + Error_code parameterize_convex_hull_with_MVC(const Triangle_mesh& mesh, const Vertex_set& vertices, const Faces_vector& faces, const CT& ct, @@ -707,7 +712,7 @@ private: public: template - Error_code parameterize(const TriangleMesh& mesh, + Error_code parameterize(const Triangle_mesh& mesh, const Vertex_set& vertices, const Faces_vector& faces, halfedge_descriptor bhd, @@ -741,15 +746,15 @@ public: return OK; } - /// Compute a one-to-one mapping from a triangular 2D surface mesh + /// computes a one-to-one mapping from a triangular 2D surface mesh /// that is not necessarily embedded to a piece of the 2D space. /// /// \tparam VertexUVmap must be a model of `ReadWritePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and - /// %Point_2 (type deduced from `TriangleMesh` using `Kernel_traits`) + /// `boost::graph_traits::%vertex_descriptor` as key type and + /// %Point_2 (type deduced from `Triangle_mesh` using `Kernel_traits`) /// as value type. /// \tparam VertexIndexMap must be a model of `ReadablePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and + /// `boost::graph_traits::%vertex_descriptor` as key type and /// a unique integer as value type. /// /// \param mesh a triangulated surface. @@ -759,7 +764,7 @@ public: /// template - Error_code parameterize(const TriangleMesh& mesh, + Error_code parameterize(const Triangle_mesh& mesh, halfedge_descriptor bhd, VertexUVMap uvmap, const VertexIndexMap vimap) diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Mean_value_coordinates_parameterizer_3.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Mean_value_coordinates_parameterizer_3.h index b832ae16ca1..796504c93f2 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Mean_value_coordinates_parameterizer_3.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Mean_value_coordinates_parameterizer_3.h @@ -45,8 +45,8 @@ namespace Surface_mesh_parameterization { /// `Fixed_border_parameterizer_3::parameterize()`. /// - It provides default `BorderParameterizer_` and `SolverTraits_` template /// parameters. -/// - It implements `compute_w_ij()` to compute w_ij = (i, j) coefficient of matrix A -/// for j neighbor vertex of i based on Floater Mean Value Coordinates parameterization. +/// - It implements `compute_w_ij()` to compute `w_ij`, the `(i,j)` coefficient of matrix `A` +/// for `j` neighbor vertex of `i` based on Floater Mean Value Coordinates parameterization. /// - It implements an optimized version of `is_one_to_one_mapping()`. /// /// \cgalModels `Parameterizer_3` @@ -110,39 +110,48 @@ public: #endif >::type Solver_traits; #else + /// The border parameterizer typedef Border_parameterizer_ Border_parameterizer; + + /// Solver traits type typedef SolverTraits_ Solver_traits; #endif + /// Triangle mesh type + typedef TriangleMesh_ Triangle_mesh; + typedef TriangleMesh_ TriangleMesh; + /// Mesh vertex type + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + + /// Mesh halfedge type + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + // Private types private: // Superclass - typedef Fixed_border_parameterizer_3 Base; + typedef Fixed_border_parameterizer_3 Base; // Private types private: - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - typedef typename boost::graph_traits::face_descriptor face_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; - typedef typename boost::graph_traits::vertex_iterator vertex_iterator; - typedef typename boost::graph_traits::face_iterator face_iterator; - typedef CGAL::Vertex_around_target_circulator vertex_around_target_circulator; + typedef typename boost::graph_traits::vertex_iterator vertex_iterator; + typedef typename boost::graph_traits::face_iterator face_iterator; + typedef CGAL::Vertex_around_target_circulator vertex_around_target_circulator; // Mesh_TriangleMesh_3 subtypes: - typedef typename Base::PPM PPM; - typedef typename Base::Kernel Kernel; - typedef typename Base::NT NT; - typedef typename Base::Point_3 Point_3; - typedef typename Base::Vector_3 Vector_3; + typedef typename Base::PPM PPM; + typedef typename Base::Kernel Kernel; + typedef typename Base::NT NT; + typedef typename Base::Point_3 Point_3; + typedef typename Base::Vector_3 Vector_3; - // Solver traits subtypes: - typedef typename Solver_traits::Vector Vector; - typedef typename Solver_traits::Matrix Matrix; + typedef typename Solver_traits::Vector Vector; + typedef typename Solver_traits::Matrix Matrix; // Public operations public: @@ -151,16 +160,16 @@ public: ///< Object that maps the surface's border to 2D space. Solver_traits sparse_la = Solver_traits()) ///< Traits object to access a sparse linear system. - : Fixed_border_parameterizer_3(border_param, sparse_la) { } // Default copy constructor and operator =() are fine - /// Check if the 3D -> 2D mapping is one-to-one. + /// returns whether the 3D -> 2D mapping is one-to-one. template - bool is_one_to_one_mapping(const TriangleMesh& mesh, + bool is_one_to_one_mapping(const Triangle_mesh& mesh, halfedge_descriptor bhd, const VertexUVMap uvmap) const { @@ -176,14 +185,14 @@ public: // Protected operations protected: - /// Compute w_ij = (i, j) coefficient of matrix A for j neighbor vertex of i. + /// computes `w_ij`, the `(i, j)`-coefficient of matrix A for j neighbor vertex of i. /// /// \param mesh a triangulated surface. /// \param main_vertex_v_i the vertex of `mesh` with index `i` /// \param neighbor_vertex_v_j the vertex of `mesh` with index `j` - virtual NT compute_w_ij(const TriangleMesh& mesh, + virtual NT compute_w_ij(const Triangle_mesh& mesh, vertex_descriptor main_vertex_v_i, - vertex_around_target_circulator neighbor_vertex_v_j) const + Vertex_around_target_circulator neighbor_vertex_v_j) const { const PPM ppmap = get(vertex_point, mesh); diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Orbifold_Tutte_parameterizer_3.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Orbifold_Tutte_parameterizer_3.h index 068a882f0e1..5d5cc9e6a54 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Orbifold_Tutte_parameterizer_3.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Orbifold_Tutte_parameterizer_3.h @@ -67,7 +67,7 @@ namespace Surface_mesh_parameterization { /// \ingroup PkgSurfaceMeshParameterizationOrbifoldHelperFunctions /// -/// Read a serie of cones from an input stream. Cones are passed as an +/// reads a serie of cones from an input stream. Cones are passed as an /// integer value that is the index of a vertex handle in the mesh tm`, using /// the vertex index property map `vpmap` for correspondency. /// @@ -192,7 +192,7 @@ Error_code read_cones(const TriangleMesh& tm, const char* filename, ConeOutputIt /// \ingroup PkgSurfaceMeshParameterizationOrbifoldHelperFunctions /// -/// Locate the cones on the seam mesh (that is, find the corresponding seam mesh +/// locates the cones on the seam mesh (that is, find the corresponding seam mesh /// `vertex_descriptor`) and mark them with a tag to indicate whether the cone is a /// simple cone or a duplicated cone (see \link PkgSurfaceMeshParameterizationEnums Cone_type \endlink). /// @@ -202,7 +202,7 @@ Error_code read_cones(const TriangleMesh& tm, const char* filename, ConeOutputIt /// but is passed here as a template parameter for convenience, to avoid /// having to pass the multiple template parameters of the class `CGAL::Seam_mesh`. /// \tparam ConeInputBidirectionalIterator must be a model of `BidirectionalIterator` -/// with value type `boost::graph_traits::%vertex_descriptor`. +/// with value type `boost::graph_traits::%vertex_descriptor`. /// \tparam ConeMap must be a model of `AssociativeContainer` /// with `boost::graph_traits::%vertex_descriptor` as key type and /// \link PkgSurfaceMeshParameterizationEnums Cone_type \endlink as value type. @@ -216,13 +216,13 @@ bool locate_cones(const SeamMesh& mesh, ConeInputBidirectionalIterator first, ConeInputBidirectionalIterator beyond, ConeMap& cones) { - typedef typename SeamMesh::TriangleMesh TriangleMesh; + typedef typename SeamMesh::Triangle_mesh Triangle_mesh; - typedef typename boost::graph_traits::vertex_descriptor TM_vertex_descriptor; + typedef typename boost::graph_traits::vertex_descriptor TM_vertex_descriptor; typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; // property map to go from TM_vertex_descriptor to Point_3 - typedef typename internal::Kernel_traits::PPM PM_PPM; + typedef typename internal::Kernel_traits::PPM PM_PPM; const PM_PPM pm_ppmap = get(boost::vertex_point, mesh.mesh()); // property map to go from vertex_descriptor to Point_3 @@ -264,9 +264,9 @@ bool locate_unordered_cones(const SeamMesh& mesh, CGAL_precondition(cones.empty()); CGAL_precondition(std::distance(first, beyond) == 3 || std::distance(first, beyond) == 4); - typedef typename SeamMesh::TriangleMesh TriangleMesh; + typedef typename SeamMesh::Triangle_mesh Triangle_mesh; - typedef typename boost::graph_traits::vertex_descriptor TM_vertex_descriptor; + typedef typename boost::graph_traits::vertex_descriptor TM_vertex_descriptor; typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; @@ -282,7 +282,7 @@ bool locate_unordered_cones(const SeamMesh& mesh, CGAL_assertion(vertex_on_seam != vertex_descriptor()); // property map to go from TM_vertex_descriptor to Point_3 - typedef typename internal::Kernel_traits::PPM PM_PPM; + typedef typename internal::Kernel_traits::PPM PM_PPM; const PM_PPM pm_ppmap = get(boost::vertex_point, mesh.mesh()); // property map to go from vertex_descriptor to Point_3 @@ -358,7 +358,8 @@ bool locate_unordered_cones(const SeamMesh& mesh, /// shows how to select cones on the input mesh and automatically construct /// the seams and the cones on the `Seam_mesh`. /// -/// \cgalModels `Parameterizer_3` +/// \attention The global function `CGAL::Surface_mesh_parameterization::parameterize()` cannot be used +/// with this parameterizer. Users should use this class's member function `parameterize()` instead. /// /// \tparam SeamMesh must be a `Seam_mesh`, with underlying mesh any model of `FaceListGraph` and `HalfedgeListGraph`. /// @@ -405,12 +406,15 @@ public: #endif >::type Solver_traits; #else + /// Solver traits type typedef SolverTraits_ Solver_traits; #endif + /// Mesh halfedge type + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + private: typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename boost::graph_traits::face_descriptor face_descriptor; typedef typename boost::graph_traits::vertex_iterator vertex_iterator; @@ -888,7 +892,7 @@ private: } public: - /// Compute a one-to-one mapping from a triangular 3D surface mesh + /// computes a one-to-one mapping from a triangular 3D surface mesh /// to a piece of the 2D space. /// The mapping is piecewise linear (linear in each triangle). /// The result is the (u,v) pair image of each vertex of the 3D surface. @@ -936,6 +940,8 @@ public: VertexUVMap uvmap, VertexIndexMap vimap) const { + CGAL_precondition(is_valid_polygon_mesh(mesh)); + CGAL_precondition(is_triangle_mesh(mesh)); CGAL_USE(bhd); Error_code status; diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Square_border_parameterizer_3.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Square_border_parameterizer_3.h index d548ecaa306..e04f4055621 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Square_border_parameterizer_3.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Square_border_parameterizer_3.h @@ -50,6 +50,10 @@ namespace Surface_mesh_parameterization { /// The user can provide four vertices on the border of the mesh, which will be /// mapped to the four corners of the square. /// +/// \attention The square border parameterizer may create degenerate faces in the parameterization: +/// if an input border vertex has valence `1` and if it is mapped to the same edge of the square +/// as its two adjacent (border) vertices, for example. +/// /// Implementation note: /// To simplify the implementation, the border parameterizer knows only the /// `TriangleMesh` class and does not know the parameterization algorithm @@ -67,18 +71,24 @@ class Square_border_parameterizer_3 { // Public types public: + /// Triangle mesh type + typedef TriangleMesh_ Triangle_mesh; + typedef TriangleMesh_ TriangleMesh; - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + /// Mesh vertex type + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef Halfedge_around_face_iterator halfedge_around_face_iterator; + /// Mesh halfedge type + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; // Protected types protected: + typedef Halfedge_around_face_iterator halfedge_around_face_iterator; + // Traits subtypes: - typedef typename internal::Kernel_traits::PPM PPM; - typedef typename internal::Kernel_traits::Kernel Kernel; + typedef typename internal::Kernel_traits::PPM PPM; + typedef typename internal::Kernel_traits::Kernel Kernel; typedef typename Kernel::FT NT; typedef typename Kernel::Point_2 Point_2; typedef typename Kernel::Vector_3 Vector_3; @@ -92,14 +102,14 @@ private: // Protected operations protected: - virtual double compute_edge_length(const TriangleMesh& mesh, + virtual double compute_edge_length(const Triangle_mesh& mesh, vertex_descriptor source, vertex_descriptor target) const = 0; // Private operations private: // Compute the total length of the border. - double compute_border_length(const TriangleMesh& mesh, + double compute_border_length(const Triangle_mesh& mesh, halfedge_descriptor bhd) const { double len = 0.0; @@ -111,7 +121,7 @@ private: // Utility method for parameterize(). // Compute the mesh iterator whose offset is closest to 'value'. - halfedge_around_face_iterator closest_iterator(const TriangleMesh& mesh, + halfedge_around_face_iterator closest_iterator(const Triangle_mesh& mesh, halfedge_descriptor bhd, Offset_map& offset, double value) const @@ -138,7 +148,7 @@ private: // Set the corners by splitting the border of the mesh in four // approximately equal segments. template - halfedge_descriptor compute_offsets_without_given_vertices(const TriangleMesh& mesh, + halfedge_descriptor compute_offsets_without_given_vertices(const Triangle_mesh& mesh, halfedge_descriptor bhd, VertexParameterizedMap vpmap, Offset_map& offset) const @@ -185,7 +195,7 @@ private: // the mesh. The vertices between two given vertices vi and vj are // sent to the same side of the square. template - halfedge_descriptor compute_offsets(const TriangleMesh& mesh, + halfedge_descriptor compute_offsets(const Triangle_mesh& mesh, halfedge_descriptor bhd, VertexParameterizedMap vpmap, Offset_map& offset) @@ -245,18 +255,18 @@ private: public: // Default constructor, copy constructor and operator =() are fine - /// Assign to the vertices of the border of the mesh a 2D position + /// assigns to the vertices of the border of the mesh a 2D position /// (i.e.\ a (u,v) pair) on the border's shape. Mark them as parameterized. /// /// \tparam VertexUVmap must be a model of `ReadWritePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and - /// %Point_2 (type deduced from `TriangleMesh` using `Kernel_traits`) + /// `boost::graph_traits::%vertex_descriptor` as key type and + /// %Point_2 (type deduced from `Triangle_mesh` using `Kernel_traits`) /// as value type. /// \tparam VertexIndexMap must be a model of `ReadablePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and + /// `boost::graph_traits::%vertex_descriptor` as key type and /// a unique integer as value type. /// \tparam VertexParameterizedMap must be a model of `ReadWritePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and + /// `boost::graph_traits::%vertex_descriptor` as key type and /// a Boolean as value type. /// /// \param mesh a triangulated surface. @@ -270,7 +280,7 @@ public: template - Error_code parameterize(const TriangleMesh& mesh, + Error_code parameterize(const Triangle_mesh& mesh, halfedge_descriptor bhd, VertexUVMap uvmap, VertexIndexMap /* vimap */, @@ -344,7 +354,7 @@ public: return OK; } - /// Indicate if the border's shape is convex. + /// indicates if the border's shape is convex. bool is_border_convex() const { return true; } public: @@ -382,6 +392,10 @@ public: /// algorithm. This class implements only `compute_edge_length()` to compute a /// segment's length. /// +/// \attention The square border parameterizer may create degenerate faces in the parameterization: +/// if an input border vertex has valence `1` and if it is mapped to the same edge of the square +/// as its two adjacent (border) vertices, for example. +/// /// \cgalModels `Parameterizer_3` /// /// \sa `CGAL::Surface_mesh_parameterization::Square_border_parameterizer_3` @@ -396,15 +410,14 @@ class Square_border_uniform_parameterizer_3 // Public types public: // We have to repeat the types exported by superclass - /// @cond SKIP_IN_MANUAL - typedef TriangleMesh_ TriangleMesh; - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - /// @endcond + typedef TriangleMesh_ TriangleMesh; + typedef TriangleMesh_ Triangle_mesh; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; // Private types private: - typedef Square_border_parameterizer_3 Base; - typedef typename Base::NT NT; + typedef Square_border_parameterizer_3 Base; + typedef typename Base::NT NT; public: virtual ~Square_border_uniform_parameterizer_3() { } @@ -425,8 +438,8 @@ public: // Protected operations protected: - /// Compute the length of an edge. - virtual NT compute_edge_length(const TriangleMesh& /* mesh */, + /// computes the length of an edge. + virtual NT compute_edge_length(const Triangle_mesh& /* mesh */, vertex_descriptor /* source */, vertex_descriptor /* target */) const { @@ -449,6 +462,10 @@ protected: /// algorithm. This class implements only `compute_edge_length()` to compute a /// segment's length. /// +/// \attention The square border parameterizer may create degenerate faces in the parameterization: +/// if an input border vertex has valence `1` and if it is mapped to the same edge of the square +/// as its two adjacent (border) vertices, for example. +/// /// \tparam TriangleMesh_ must be a model of `FaceGraph`. /// /// \cgalModels `Parameterizer_3` @@ -462,14 +479,13 @@ class Square_border_arc_length_parameterizer_3 { // Public types public: - /// @cond SKIP_IN_MANUAL - typedef TriangleMesh_ TriangleMesh; - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - /// @endcond + typedef TriangleMesh_ TriangleMesh; + typedef TriangleMesh_ Triangle_mesh; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; // Private types private: - typedef Square_border_parameterizer_3 Base; + typedef Square_border_parameterizer_3 Base; typedef typename Base::PPM PPM; typedef typename Base::NT NT; typedef typename Base::Vector_3 Vector_3; @@ -494,7 +510,7 @@ public: // Protected operations protected: /// Compute the length of an edge. - virtual NT compute_edge_length(const TriangleMesh& mesh, + virtual NT compute_edge_length(const Triangle_mesh& mesh, vertex_descriptor source, vertex_descriptor target) const { diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Two_vertices_parameterizer_3.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Two_vertices_parameterizer_3.h index 6fcadcd5eba..7a25244f443 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Two_vertices_parameterizer_3.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Two_vertices_parameterizer_3.h @@ -56,16 +56,22 @@ class Two_vertices_parameterizer_3 { // Public types public: + /// Triangle mesh type + typedef TriangleMesh_ Triangle_mesh; + typedef TriangleMesh_ TriangleMesh; - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + /// Mesh vertex type + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + + /// Mesh halfedge type + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; // Private types private: // Traits subtypes: - typedef typename internal::Kernel_traits::PPM PPM; - typedef typename internal::Kernel_traits::Kernel Kernel; + typedef typename internal::Kernel_traits::PPM PPM; + typedef typename internal::Kernel_traits::Kernel Kernel; typedef typename Kernel::FT NT; typedef typename Kernel::Point_2 Point_2; typedef typename Kernel::Point_3 Point_3; @@ -92,7 +98,7 @@ public: typename VertexUVmap, typename VertexIndexMap, typename VertexParameterizedMap> - Error_code parameterize(const TriangleMesh& mesh, + Error_code parameterize(const Triangle_mesh& mesh, const VertexContainer& vertices, VertexUVmap uvmap, VertexIndexMap /* vimap */, @@ -267,17 +273,17 @@ public: return OK; } - /// Map two extreme vertices of the 3D mesh and mark them as parameterized. + /// maps two extreme vertices of the 3D mesh and mark them as parameterized. /// /// \tparam VertexUVmap must be a model of `ReadWritePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and - /// %Point_2 (type deduced from `TriangleMesh` using `Kernel_traits`) + /// `boost::graph_traits::%vertex_descriptor` as key type and + /// %Point_2 (type deduced from `Triangle_mesh` using `Kernel_traits`) /// as value type. /// \tparam VertexIndexMap must be a model of `ReadablePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and + /// `boost::graph_traits::%vertex_descriptor` as key type and /// a unique integer as value type. /// \tparam VertexParameterizedMap must be a model of `ReadWritePropertyMap` with - /// `boost::graph_traits::%vertex_descriptor` as key type and + /// `boost::graph_traits::%vertex_descriptor` as key type and /// a Boolean as value type. /// /// \param mesh a triangulated surface. @@ -292,7 +298,7 @@ public: template - Error_code parameterize(const TriangleMesh& mesh, + Error_code parameterize(const Triangle_mesh& mesh, halfedge_descriptor bhd, VertexUVmap uvmap, VertexIndexMap vimap, @@ -300,7 +306,7 @@ public: { // Fill containers boost::unordered_set vertices; - internal::Containers_filler fc(mesh, vertices); + internal::Containers_filler fc(mesh, vertices); Polygon_mesh_processing::connected_component( face(opposite(bhd, mesh), mesh), mesh, @@ -309,7 +315,7 @@ public: return parameterize(mesh, vertices, uvmap, vimap, vpmap); } - /// Indicate if the border's shape is convex. + /// indicates if the border's shape is convex. /// Meaningless for free border parameterization algorithms. bool is_border_convex() const { return false; } }; diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/internal/Containers_filler.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/internal/Containers_filler.h index 2cbb2949017..9e9d5422024 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/internal/Containers_filler.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/internal/Containers_filler.h @@ -14,11 +14,13 @@ #include -#include +#include +#include -#include "boost/tuple/tuple.hpp" +#include #include #include + #include namespace CGAL { @@ -56,7 +58,7 @@ public: : mesh(mesh_), vertices(vertices_), faces(nullptr) { } - void operator()(face_descriptor fd) + void operator()(const face_descriptor fd) { halfedge_descriptor hd = halfedge(fd, mesh); for(vertex_descriptor vd : vertices_around_face(hd, mesh)) { @@ -78,10 +80,10 @@ struct Index_map_filler : mesh(mesh), map(&map), index(0) { } - void operator()(const face_descriptor& fd) + void operator()(const face_descriptor fd) { - for(vertex_descriptor vd : - vertices_around_face(halfedge(fd, mesh), mesh)) { + for(vertex_descriptor vd : vertices_around_face(halfedge(fd, mesh), mesh)) + { typename Map::iterator it; bool new_element; boost::tie(it,new_element) = map->insert(std::make_pair(vd,1)); @@ -96,12 +98,47 @@ struct Index_map_filler int index; }; +template +void fill_index_map_of_cc(const typename boost::graph_traits::halfedge_descriptor bhd, + const TriangleMesh& mesh, + VertexIndexMap vimap) +{ + namespace PMP = CGAL::Polygon_mesh_processing; + + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + std::vector CC_faces; + + // 'reserve' might cause a huge amount of memory to be used for a tiny CC, + // but if this is a problem as a user, one could simply parameterize a Face_filtered_graph instead. + CC_faces.reserve(num_faces(mesh)); + + PMP::connected_component(face(opposite(bhd, mesh), mesh), mesh, std::back_inserter(CC_faces)); + + // If all vertices are involved, avoid walking all the faces + if(CC_faces.size() == faces(mesh).size()) + { + BGL::internal::Index_map_initializer id_initializer; + id_initializer(CGAL::internal_np::vertex_index, vimap, mesh); + } + else + { + for(vertex_descriptor v : vertices(mesh)) + put(vimap, v, -1); + + int index = 0; + for(face_descriptor f : CC_faces) + for(vertex_descriptor v : vertices_around_face(halfedge(f, mesh), mesh)) + if(get(vimap, v) == -1) + put(vimap, v, index++); + } +} + } // namespace internal } // namespace Surface_mesh_parameterization } // namespace CGAL -#include - #endif // CGAL_SURFACE_MESH_PARAMETERIZATION_INTERNAL_CONTAINERS_FILLER_H diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/internal/validity.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/internal/validity.h index 2398eff4a91..21373631533 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/internal/validity.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/internal/validity.h @@ -232,7 +232,7 @@ public: { } }; -/// Check if the 3D -> 2D mapping is one-to-one. +/// returns whether the 3D -> 2D mapping is one-to-one. /// This function is stronger than "has_flips()" because the parameterized /// surface can loop over itself without creating any flips. template + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace CGAL { +namespace Surface_mesh_parameterization { + +#ifndef DOXYGEN_RUNNING + +// Measure L2 stretch +template +double compute_L2_stretch(const VertexRange& vertex_range, + const FaceRange& face_range, + const TriangleMesh& tmesh, + const VertexUVmap uvmap) +{ + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::graph_traits::face_descriptor face_descriptor; + + typedef typename boost::property_map::const_type VertexPointMap; + typedef typename boost::property_traits::value_type Point_3; + typedef typename CGAL::Kernel_traits::Kernel Kernel; + typedef typename Kernel::Point_2 Point_2; + + typedef CGAL::dynamic_face_property_t Face_double_tag; + typedef typename boost::property_map::const_type Face_double_map; + + Face_double_map area_2D = get(Face_double_tag(), tmesh); + Face_double_map area_3D = get(Face_double_tag(), tmesh); + + // iterate fpr all inner vertices and for each vertex + std::vector area_dist; + + double A_3D = 0.; + double A_2D = 0.; + + for(face_descriptor f : face_range) + { + std::vector uv_points; + for(vertex_descriptor v : vertices_around_face(halfedge(f, tmesh), tmesh)) + uv_points.push_back(get(uvmap, v)); + + const double a_2D = abs(CGAL::area(get(uvmap, target(halfedge(f, tmesh), tmesh)), + get(uvmap, source(halfedge(f, tmesh), tmesh)), + get(uvmap, target(next(halfedge(f, tmesh), tmesh), tmesh)))); + const double a_3D = Polygon_mesh_processing::face_area(f, tmesh); + + put(area_2D, f, a_2D); + put(area_3D, f, a_3D); + + A_2D += a_2D; + A_3D += a_3D; + } + + for(vertex_descriptor v : vertex_range) + { + // inner vertices only + if(CGAL::is_border(v, tmesh)) + continue; + + double a_2D = 0.; + double a_3D = 0.; + + // find the area of all the adjacent faces to this vertex + CGAL::Face_around_target_circulator f_j(halfedge(v, tmesh), tmesh), end = f_j; + CGAL_For_all(f_j, end) + { + if(*f_j == boost::graph_traits::null_face()) + continue; + + a_2D += get(area_2D, *f_j); + a_3D += get(area_3D, *f_j); + } + + a_2D /= A_2D; + a_3D /= A_3D; + + area_dist.push_back(square((a_3D/a_2D) - 1.)); + } + + return sqrt(std::accumulate(area_dist.begin(), area_dist.end(), 0.)); +} + +template +double compute_L2_stretch(const TriangleMesh& tmesh, + const VertexUVmap uvmap) +{ + return compute_L2_stretch(vertices(tmesh), faces(tmesh), tmesh, uvmap); +} + +#endif // DOXYGEN_RUNNING + +} // namespace Surface_mesh_parameterization +} // namespace CGAL + +#endif // CGAL_SURFACE_MESH_PARAMETERIZATION_INTERNAL_DISTORTION_H diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/orbifold_shortest_path.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/orbifold_shortest_path.h index 53a5bfa2a3c..4243374f9d3 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/orbifold_shortest_path.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/orbifold_shortest_path.h @@ -103,7 +103,7 @@ public: /// \ingroup PkgSurfaceMeshParameterizationOrbifoldHelperFunctions /// -/// Compute the shortest path between `source` and `target` over `mesh`, using +/// computes the shortest path between `source` and `target` over `mesh`, using /// /// boost::dijkstra_shortest_paths(). /// diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/parameterize.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/parameterize.h index 12a47ee05a7..a669eb417f6 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/parameterize.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/parameterize.h @@ -15,19 +15,10 @@ #include -#include - -#include - #include #include -#include - -#include #include -#include -#include /// \file parameterize.h @@ -37,7 +28,7 @@ namespace Surface_mesh_parameterization { /// \ingroup PkgSurfaceMeshParameterizationMainFunction /// -/// Compute a one-to-one mapping from a 3D triangle surface `mesh` to a +/// computes a one-to-one mapping from a 3D triangle surface `mesh` to a /// simple 2D domain. /// The mapping is piecewise linear on the triangle mesh. /// The result is a pair `(u,v)` of parameter coordinates for each vertex of the input mesh. @@ -57,7 +48,7 @@ namespace Surface_mesh_parameterization { /// \param mesh a triangulated surface. /// \param parameterizer a parameterizer. /// \param bhd a halfedge descriptor on the boundary of `mesh`. -/// \param uvm an instanciation of the class `VertexUVmap`. +/// \param uvmap an instanciation of the class `VertexUVmap`. /// /// \pre `mesh` must be a triangular mesh. /// \pre The mesh border must be mapped onto a convex polygon @@ -68,28 +59,26 @@ template Error_code parameterize(TriangleMesh& mesh, Parameterizer parameterizer, HD bhd, - VertexUVmap uvm) + VertexUVmap uvmap) { - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + CGAL_precondition(is_valid_polygon_mesh(mesh)); + CGAL_precondition(bhd != boost::graph_traits::null_halfedge() && is_border(bhd, mesh)); - typedef boost::unordered_map Indices; - Indices indices; - CGAL::Polygon_mesh_processing::connected_component( - face(opposite(bhd, mesh), mesh), - mesh, - boost::make_function_output_iterator( - internal::Index_map_filler(mesh, indices))); - boost::associative_property_map vipm(indices); + typedef CGAL::dynamic_vertex_property_t Vertex_int_tag; + typedef typename boost::property_map::type Vertex_int_map; + Vertex_int_map vimap = get(Vertex_int_tag(), mesh); + internal::fill_index_map_of_cc(bhd, mesh, vimap); - boost::unordered_set vs; - internal::Bool_property_map > vpm(vs); + typedef CGAL::dynamic_vertex_property_t Vertex_bool_tag; + typedef typename boost::property_map::type Vertex_bool_map; + Vertex_bool_map vpmap = get(Vertex_bool_tag(), mesh); - return parameterizer.parameterize(mesh, bhd, uvm, vipm, vpm); + return parameterizer.parameterize(mesh, bhd, uvmap, vimap, vpmap); } /// \ingroup PkgSurfaceMeshParameterizationMainFunction /// -/// Compute a one-to-one mapping from a 3D triangle surface `mesh` to a +/// computes a one-to-one mapping from a 3D triangle surface `mesh` to a /// 2D circle, using Floater Mean Value Coordinates algorithm. /// A one-to-one mapping is guaranteed. /// @@ -106,7 +95,7 @@ Error_code parameterize(TriangleMesh& mesh, /// /// \param mesh a triangulated surface. /// \param bhd a halfedge descriptor on the boundary of `mesh`. -/// \param uvm an instanciation of the class `VertexUVmap`. +/// \param uvmap an instanciation of the class `VertexUVmap`. /// /// \pre `mesh` must be a triangular mesh. /// \pre The vertices must be indexed (vimap must be initialized). @@ -114,51 +103,14 @@ Error_code parameterize(TriangleMesh& mesh, template Error_code parameterize(TriangleMesh& mesh, HD bhd, - VertexUVmap uvm) + VertexUVmap uvmap) { Mean_value_coordinates_parameterizer_3 parameterizer; - return parameterize(mesh, parameterizer, bhd, uvm); -} - -template -class Seam_mesh; - -template -Error_code parameterize(Seam_mesh& mesh, - Parameterizer parameterizer, - HD bhd, - VertexUVmap uvm) -{ - typedef typename boost::graph_traits >::vertex_descriptor vertex_descriptor; - boost::unordered_set vs; - internal::Bool_property_map > vpm(vs); - - typedef boost::unordered_map Indices; - Indices indices; - CGAL::Polygon_mesh_processing::connected_component( - face(opposite(bhd, mesh), mesh), - mesh, - boost::make_function_output_iterator( - internal::Index_map_filler, - Indices>(mesh, indices))); - boost::associative_property_map vipm(indices); - - return parameterizer.parameterize(mesh, bhd, uvm, vipm, vpm); -} - -template -Error_code parameterize(Seam_mesh& mesh, - HD bhd, - VertexUVmap uvm) -{ - Mean_value_coordinates_parameterizer_3 > parameterizer; - return parameterize(mesh, parameterizer, bhd, uvm); + return parameterize(mesh, parameterizer, bhd, uvmap); } } // namespace Surface_mesh_parameterization } // namespace CGAL -#include - #endif // CGAL_PARAMETERIZE_H diff --git a/Surface_mesh_parameterization/include/CGAL/surface_mesh_parameterization.h b/Surface_mesh_parameterization/include/CGAL/surface_mesh_parameterization.h index 47c47969680..fb2cea903d8 100644 --- a/Surface_mesh_parameterization/include/CGAL/surface_mesh_parameterization.h +++ b/Surface_mesh_parameterization/include/CGAL/surface_mesh_parameterization.h @@ -22,12 +22,14 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include diff --git a/Surface_mesh_parameterization/test/Surface_mesh_parameterization/extensive_parameterization_test.cpp b/Surface_mesh_parameterization/test/Surface_mesh_parameterization/extensive_parameterization_test.cpp index 43cd310ad40..79187d8c4d7 100644 --- a/Surface_mesh_parameterization/test/Surface_mesh_parameterization/extensive_parameterization_test.cpp +++ b/Surface_mesh_parameterization/test/Surface_mesh_parameterization/extensive_parameterization_test.cpp @@ -10,6 +10,7 @@ #include #include +#include #include @@ -17,6 +18,7 @@ #include namespace SMP = CGAL::Surface_mesh_parameterization; +namespace PMP = CGAL::Polygon_mesh_processing; typedef CGAL::Simple_cartesian Kernel; typedef Kernel::Point_2 Point_2; @@ -29,6 +31,7 @@ typedef Kernel::Point_3 Point_3; #define DCM_PM_SEAM_MESH #define DAC_SM_SEAM_MESH #define ORBIFOLD_SM_MESH +#define ITERATIVE_SURF_MESH // POLYHEDRON_MESH typedef CGAL::Polyhedron_3 PMesh; @@ -94,7 +97,7 @@ int main(int, char**) return EXIT_FAILURE; } - PM_halfedge_descriptor hd = CGAL::Polygon_mesh_processing::longest_border(pm).first; + PM_halfedge_descriptor hd = PMP::longest_border(pm).first; CGAL::Unique_hash_map > uvhm; @@ -113,7 +116,7 @@ int main(int, char**) std::cout << "Parameterized with MVC (POLY)!" << std::endl; } } -#endif +#endif // MVC_POLYHEDRON_MESH // *************************************************************************** // ARAP WITH POLYHEDRON_MESH @@ -131,7 +134,7 @@ int main(int, char**) return EXIT_FAILURE; } - PM_halfedge_descriptor hd = CGAL::Polygon_mesh_processing::longest_border(pm).first; + PM_halfedge_descriptor hd = PMP::longest_border(pm).first; // UV map CGAL::Unique_hash_map > > uvpm(uvhm); // Indices map - typedef boost::unordered_map Indices; - Indices indices; - CGAL::Polygon_mesh_processing::connected_component( - face(opposite(hd, pm), pm), - pm, - boost::make_function_output_iterator( - SMP::internal::Index_map_filler(pm, indices))); - - boost::associative_property_map vipm(indices); + typedef CGAL::dynamic_vertex_property_t Vertex_int_tag; + typedef typename boost::property_map::type Vertex_int_map; + Vertex_int_map vipm = get(Vertex_int_tag(), pm); + CGAL::Surface_mesh_parameterization::internal::fill_index_map_of_cc(hd, pm, vipm); // Vertex parameterized map - boost::unordered_set vs; - SMP::internal::Bool_property_map > vpm(vs); + typedef CGAL::dynamic_vertex_property_t Vertex_bool_tag; + typedef typename boost::property_map::type Vertex_bool_map; + Vertex_bool_map vpm = get(Vertex_bool_tag(), pm); // Parameterizer SMP::ARAP_parameterizer_3 parameterizer; SMP::Error_code status = parameterizer.parameterize(pm, hd, uvpm, vipm, vpm); - - if(status != SMP::OK) { + SMP::Error_code status_bis = SMP::parameterize(pm, parameterizer, hd, uvpm); + if(status != SMP::OK || status_bis != SMP::OK) { std::cout << "Encountered a problem: " << status << std::endl; return EXIT_FAILURE; } @@ -167,7 +166,7 @@ int main(int, char**) std::cout << "Parameterized with ARAP (POLY)!" << std::endl; } } -#endif +#endif // ARAP_POLYHEDRON_MESH // *************************************************************************** // Barycentric mapping @@ -185,7 +184,7 @@ int main(int, char**) return EXIT_FAILURE; } - SM_halfedge_descriptor hd = CGAL::Polygon_mesh_processing::longest_border(sm).first; + SM_halfedge_descriptor hd = PMP::longest_border(sm).first; assert(hd != SM_halfedge_descriptor()); // UV map @@ -195,11 +194,9 @@ int main(int, char**) // Indices map typedef boost::unordered_map Indices; Indices indices; - CGAL::Polygon_mesh_processing::connected_component( - face(opposite(hd, sm), sm), - sm, - boost::make_function_output_iterator( - SMP::internal::Index_map_filler(sm, indices))); + PMP::connected_component(face(opposite(hd, sm), sm), sm, + boost::make_function_output_iterator( + SMP::internal::Index_map_filler(sm, indices))); boost::associative_property_map vipm(indices); // Vertex parameterized map @@ -210,8 +207,9 @@ int main(int, char**) SMP::Barycentric_mapping_parameterizer_3 parameterizer; SMP::Error_code status = parameterizer.parameterize(sm, hd, uvpm, vipm, vpm); + SMP::Error_code status_bis = SMP::parameterize(sm, parameterizer, hd, uvpm); - if(status != SMP::OK) { + if(status != SMP::OK || status_bis != SMP::OK) { std::cout << "Encountered a problem: " << status << std::endl; return EXIT_FAILURE; } @@ -219,7 +217,7 @@ int main(int, char**) std::cout << "Parameterized with Barycentric (SM)!" << std::endl; } } -#endif +#endif // BARY_SURF_MESH // *************************************************************************** // ARAP WITH SURF_MESH @@ -238,8 +236,7 @@ int main(int, char**) } // halfedge on the longest border - SM_halfedge_descriptor hd = - CGAL::Polygon_mesh_processing::longest_border(sm).first; + SM_halfedge_descriptor hd = PMP::longest_border(sm).first; CGAL::Unique_hash_map > uvhm; @@ -251,11 +248,9 @@ int main(int, char**) // Indices map typedef boost::unordered_map Indices; Indices indices; - CGAL::Polygon_mesh_processing::connected_component( - face(opposite(hd, sm), sm), - sm, - boost::make_function_output_iterator( - SMP::internal::Index_map_filler(sm, indices))); + PMP::connected_component(face(opposite(hd, sm), sm), sm, + boost::make_function_output_iterator( + SMP::internal::Index_map_filler(sm, indices))); boost::associative_property_map vipm(indices); // Parameterized bool pmap @@ -266,7 +261,9 @@ int main(int, char**) SMP::ARAP_parameterizer_3 parameterizer; SMP::Error_code status = parameterizer.parameterize(sm, hd, uv_pm, vipm, vpm); - if(status != SMP::OK) { + SMP::Error_code status_bis = SMP::parameterize(sm, parameterizer, hd, uv_pm); + + if(status != SMP::OK || status_bis != SMP::OK) { std::cout << "Encountered a problem: " << status << std::endl; return EXIT_FAILURE; } @@ -274,7 +271,7 @@ int main(int, char**) std::cout << "Parameterized with ARAP (SM)!" << std::endl; } } -#endif +#endif // ARAP_SURF_MESH #ifdef DCM_PM_SEAM_MESH { @@ -307,16 +304,14 @@ int main(int, char**) PM_UV_pmap uv_pm(uv_hm); // a halfedge on the (possibly virtual) border - PM_SE_halfedge_descriptor hd = CGAL::Polygon_mesh_processing::longest_border(mesh).first; + PM_SE_halfedge_descriptor hd = PMP::longest_border(mesh).first; // Indices typedef boost::unordered_map Indices; Indices indices; - CGAL::Polygon_mesh_processing::connected_component( - face(opposite(hd, mesh), mesh), - mesh, - boost::make_function_output_iterator( - SMP::internal::Index_map_filler(mesh, indices))); + PMP::connected_component(face(opposite(hd, mesh), mesh), mesh, + boost::make_function_output_iterator( + SMP::internal::Index_map_filler(mesh, indices))); boost::associative_property_map vipm(indices); // Parameterized @@ -326,8 +321,9 @@ int main(int, char**) SMP::Discrete_conformal_map_parameterizer_3 parameterizer; SMP::Error_code status = parameterizer.parameterize(mesh, hd, uv_pm, vipm, vpm); + SMP::Error_code status_bis = SMP::parameterize(mesh, parameterizer, hd, uv_pm); - if(status != SMP::OK) { + if(status != SMP::OK || status_bis != SMP::OK) { std::cout << "Encountered a problem: " << status << std::endl; return EXIT_FAILURE; } @@ -335,7 +331,7 @@ int main(int, char**) std::cout << "Parameterized with DCM (SEAM POLY)!" << std::endl; } } -#endif +#endif // DCM_PM_SEAM_MESH // *************************************************************************** // DAC WITH SEAM_MESH (SM) @@ -373,16 +369,14 @@ int main(int, char**) Point_2>("h:uv").first; // a halfedge on the (possibly virtual) border - SM_SE_halfedge_descriptor hd = CGAL::Polygon_mesh_processing::longest_border(mesh).first; + SM_SE_halfedge_descriptor hd = PMP::longest_border(mesh).first; // Indices typedef boost::unordered_map Indices; Indices indices; - CGAL::Polygon_mesh_processing::connected_component( - face(opposite(hd, mesh), mesh), - mesh, - boost::make_function_output_iterator( - SMP::internal::Index_map_filler(mesh, indices))); + PMP::connected_component(face(opposite(hd, mesh), mesh), mesh, + boost::make_function_output_iterator( + SMP::internal::Index_map_filler(mesh, indices))); boost::associative_property_map vipm(indices); // Parameterized @@ -392,7 +386,9 @@ int main(int, char**) SMP::Discrete_authalic_parameterizer_3 parameterizer; SMP::Error_code status = parameterizer.parameterize(mesh, hd, uv_pm, vipm, vpm); - if(status != SMP::OK) { + SMP::Error_code status_bis = SMP::parameterize(mesh, parameterizer, hd, uv_pm); + + if(status != SMP::OK || status_bis != SMP::OK) { std::cout << "Encountered a problem: " << status << std::endl; return EXIT_FAILURE; } @@ -400,7 +396,7 @@ int main(int, char**) std::cout << "Parameterized with DAC (SEAM SM)!" << std::endl; } } -#endif +#endif // DAC_SM_SEAM_MESH #ifdef ORBIFOLD_SM_MESH { @@ -464,10 +460,10 @@ int main(int, char**) // a halfedge on the (possibly virtual) border // only used in output (will also be used to handle multiple connected components in the future) - SM_SE_halfedge_descriptor hd = CGAL::Polygon_mesh_processing::longest_border(mesh, - CGAL::Polygon_mesh_processing::parameters::all_default()).first; + SM_SE_halfedge_descriptor hd = PMP::longest_border(mesh, PMP::parameters::all_default()).first; SMP::Error_code status = parameterizer.parameterize(mesh, hd, cmap, uvmap, vimap); + if(status != SMP::OK) { std::cout << "Encountered a problem: " << status << std::endl; return EXIT_FAILURE; @@ -476,7 +472,60 @@ int main(int, char**) std::cout << "Parameterized with Orbifold (SEAM SM)!" << std::endl; } } -#endif +#endif // ORBIFOLD_SM_MESH + + // *************************************************************************** + // ITERATIVE AUTHALIC WITH SURFACE_MESH + // *************************************************************************** + +#ifdef ITERATIVE_SURF_MESH + { + std::cout << " ----------- ITERATIVE AUTHALIC SURFACE MESH ----------- " << std::endl; + + std::ifstream in("data/oni.off"); + SMesh sm; + in >> sm; + if(!in || num_vertices(sm) == 0) { + std::cerr << "Problem loading the input data" << std::endl; + return EXIT_FAILURE; + } + + SM_halfedge_descriptor hd = PMP::longest_border(sm).first; + assert(hd != SM_halfedge_descriptor()); + + // UV map + typedef SMesh::Property_map UV_pmap; + UV_pmap uvpm = sm.add_property_map("h:uv").first; + + // Indices map + typedef boost::unordered_map Indices; + Indices indices; + PMP::connected_component(face(opposite(hd, sm), sm), sm, + boost::make_function_output_iterator( + SMP::internal::Index_map_filler(sm, indices))); + boost::associative_property_map vipm(indices); + + // Vertex parameterized map + boost::unordered_set vs; + SMP::internal::Bool_property_map > vpm(vs); + + // Parameterizer + SMP::Iterative_authalic_parameterizer_3 parameterizer; + + double error = 0; + unsigned int iterations = 15; + SMP::Error_code status = parameterizer.parameterize(sm, hd, uvpm, vipm, vpm, iterations, error); + SMP::Error_code status_bis = parameterizer.parameterize(sm, uvpm, 10); + + if(status != SMP::OK || status_bis != SMP::OK) { + std::cout << "Encountered a problem: " << status << std::endl; + return EXIT_FAILURE; + } + else { + std::cout << "Parameterized with Barycentric (SM)!" << std::endl; + } + } +#endif // DAC_SM_SEAM_MESH std::cout << "Done!" << std::endl;