From b352738d709bce9f9de65c89716e51231f44d414 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Mon, 29 Jan 2018 18:32:25 +0100 Subject: [PATCH] Documented some functions used in SMP/Orbifold Tutte Embedding parameterizer Along the way, fix a few mistakes and generalize the API of these newly documented functions. --- .../Surface_mesh/Parameterization_plugin.cpp | 8 +- .../PackageDescription.txt | 31 +- .../fig/orbifold_path.svg | 6 +- .../orbifold.cpp | 26 +- .../Error_code.h | 5 +- .../Orbifold_Tutte_parameterizer_3.h | 302 ++++++++++++++++-- .../internal/orbifold_cone_helper.h | 271 +--------------- .../orbifold_enums.h | 87 +++++ ...ortest_path.h => orbifold_shortest_path.h} | 74 +++-- .../CGAL/surface_mesh_parameterization.h | 1 + .../extensive_parameterization_test.cpp | 15 +- 11 files changed, 489 insertions(+), 337 deletions(-) create mode 100644 Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/orbifold_enums.h rename Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/{internal/shortest_path.h => orbifold_shortest_path.h} (61%) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh/Parameterization_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh/Parameterization_plugin.cpp index da54b696dcd..869a21ec7c9 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh/Parameterization_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/Surface_mesh/Parameterization_plugin.cpp @@ -950,12 +950,8 @@ void Polyhedron_demo_parameterization_plugin::parameterize(const Parameterizatio Parameterizer parameterizer(orb); // mark cones in the seam mesh - typedef boost::unordered_map Cones; - Cones cmap; - - if(!SMP::internal::locate_unordered_cones, - Cones>(sMesh, unordered_cones, cmap)) + boost::unordered_map cmap; + if(!SMP::locate_unordered_cones(sMesh, unordered_cones.begin(), unordered_cones.end(), cmap)) return; // vimap and uvmap diff --git a/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/PackageDescription.txt b/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/PackageDescription.txt index d1483121633..a1372dac68f 100644 --- a/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/PackageDescription.txt +++ b/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/PackageDescription.txt @@ -112,20 +112,32 @@ The package performs the next checks: - the input mesh is a surface with one connected component. */ -/// \defgroup PkgSurfaceParameterizationMainFunction Main Function -/// \ingroup PkgSurfaceParameterization +/*! + \defgroup PkgSurfaceParameterizationMainFunction Main Functions + \ingroup PkgSurfaceParameterization -/// \defgroup PkgSurfaceParameterizationEnums Enums -/// \ingroup PkgSurfaceParameterization + The central functions, main entry point of the package. +*/ -/// \defgroup PkgSurfaceParameterizationConcepts Concepts -/// \ingroup PkgSurfaceParameterization +/*! + \defgroup PkgSurfaceParameterizationEnums Enums + \ingroup PkgSurfaceParameterization + + The enum classes defined and used in this package. +*/ + +/*! + \defgroup PkgSurfaceParameterizationConcepts Concepts + \ingroup PkgSurfaceParameterization + + The concepts of this package. +*/ /*! \defgroup PkgSurfaceParameterizationMethods Surface Parameterization Methods \ingroup PkgSurfaceParameterization -This \cgal package implements several parameterization methods: + This \cgal package implements several parameterization methods: - Fixed border: - Tutte Barycentric Mapping \cgalCite{t-hdg-63}. @@ -162,5 +174,10 @@ This package implements common border parameterization methods: vertices). */ +/*! + \defgroup PkgSurfaceParameterizationOrbifoldHelperFunctions Orbifold Helper Functions + \ingroup PkgSurfaceParameterizationMethods + + */ diff --git a/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/fig/orbifold_path.svg b/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/fig/orbifold_path.svg index 33be0c78eae..10b5bfe99cd 100644 --- a/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/fig/orbifold_path.svg +++ b/Surface_mesh_parameterization/doc/Surface_mesh_parameterization/fig/orbifold_path.svg @@ -1,5 +1,5 @@ - + @@ -85,7 +85,7 @@ - + @@ -179,7 +179,7 @@ - + diff --git a/Surface_mesh_parameterization/examples/Surface_mesh_parameterization/orbifold.cpp b/Surface_mesh_parameterization/examples/Surface_mesh_parameterization/orbifold.cpp index b9da99fc9e1..84296d8ab87 100644 --- a/Surface_mesh_parameterization/examples/Surface_mesh_parameterization/orbifold.cpp +++ b/Surface_mesh_parameterization/examples/Surface_mesh_parameterization/orbifold.cpp @@ -3,10 +3,6 @@ #include #include -#include -#include -#include - #include #include @@ -66,10 +62,9 @@ int main(int argc, char * argv[]) // -- the third line optionally provides the seam edges indices as 'e11 e12 e21 e22 e31 e32' etc. const char* cone_filename = (argc>2) ? argv[2] : "data/bear.selection.txt"; - // Read the cones and find the corresponding vertex_descriptor in the underlying mesh 'sm' - typedef std::vector Cones_in_smesh_container; - Cones_in_smesh_container cone_sm_vds; - SMP::internal::read_cones(sm, cone_filename, cone_sm_vds); + // Read the cones and compute their corresponding vertex_descriptor in the underlying mesh 'sm' + std::vector cone_sm_vds; + SMP::read_cones(sm, cone_filename, std::back_inserter(cone_sm_vds)); // Two property maps to store the seam edges and vertices Seam_edge_pmap seam_edge_pm = sm.add_property_map("e:on_seam", false).first; @@ -78,12 +73,14 @@ int main(int argc, char * argv[]) // The seam mesh Mesh mesh(sm, seam_edge_pm, seam_vertex_pm); - // Use the path provided between cones to create a seam mesh + // If provided, use the path between cones to create a seam mesh SM_halfedge_descriptor smhd = mesh.add_seams(cone_filename); + + // If not provided, compute the paths using shortest paths if(smhd == SM_halfedge_descriptor() ) { - std::cout << "No seams were given in input, computing shortest paths between cones" << std::endl; + std::cout << "No seams given in input, computing the shortest paths between consecutive cones" << std::endl; std::list seam_edges; - SMP::internal::compute_shortest_paths_between_cones(sm, cone_sm_vds, seam_edges); + SMP::compute_shortest_paths_between_cones(sm, cone_sm_vds.begin(), cone_sm_vds.end(), seam_edges); // Add the seams to the seam mesh BOOST_FOREACH(SM_edge_descriptor e, seam_edges) { @@ -103,11 +100,8 @@ int main(int argc, char * argv[]) } // Mark the cones in the seam mesh - typedef boost::unordered_map Cones; - Cones cmap; - SMP::internal::locate_cones(mesh, cone_sm_vds, cmap); + boost::unordered_map cmap; + SMP::locate_cones(mesh, cone_sm_vds.begin(), cone_sm_vds.end(), cmap); // The 2D points of the uv parametrisation will be written into this map // Note that this is a halfedge property map, and that uv values diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Error_code.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Error_code.h index 62622e1f99e..658d6b72ea8 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Error_code.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/Error_code.h @@ -41,9 +41,10 @@ enum Error_code ERROR_WRONG_PARAMETER ///< A method received an unexpected parameter }; -/// Get message corresponding to an error code +/// \ingroup PkgSurfaceParameterizationEnums +/// \brief Get the message corresponding to an error code. /// \param error_code The code returned by `parameterize()` -/// \return The string describing the error code +/// \return The string describing the error code. const char* get_error_message(int error_code) { // Messages corresponding to Error_code list above. Must be kept in sync! 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 791762ee264..65d4a9a8a35 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 @@ -24,7 +24,9 @@ #include #include +#include #include +#include #include @@ -64,20 +66,276 @@ namespace CGAL { namespace Surface_mesh_parameterization { -/// \ingroup PkgSurfaceParameterizationEnums +/// \ingroup PkgSurfaceParameterizationOrbifoldHelperFunctions /// -/// Weight type used in the parameterization computation. +/// Read 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 `pm`, using +/// the vertex index property map `vpmap` for correspondency. /// -/// MVC weights are guaranteed to generate positive edge weights, and the parameterization -/// is guaranteed to be injective. +/// \attention The mesh is here `sm`, it is the base mesh of the CGAL::Seam_mesh +/// that is passed in input, not the seam mesh itself. /// -/// In case the cotangent weights are used, the orbifold-Tutte embedding globally -/// minimizes the Dirichlet energy and approximates conformal mappings. -enum Weight_type +/// \tparam TriangleMesh A triangle mesh, model of `FaceListGraph` and `HalfedgeListGraph`. +/// \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 ConeOutputInserter a model of `OutputIterator` with value type +/// `boost::graph_traits::%vertex_descriptor`. +/// +/// \param tm the triangular mesh to be parameterized +/// \param in the input stream +/// \param vpmap an initialized vertex - index map +/// \param out the output iterator +/// +/// \pre The number of cones must match the chosen \link PkgSurfaceParameterizationEnums Orbifold_type \endlink. +/// \pre No two cones correspond to the same vertex (all cones have different index). +/// +/// \return The corresponding vertex descriptors are output, in the same order as the input integers, in `out`. +/// The function checks if the input is valid (no duplicate, correct number of cones) and returns an `Error_code`. +template +Error_code read_cones(const TriangleMesh& tm, std::ifstream& in, VertexIndexMap vpmap, ConeOutputInserter out) { - Cotangent = 0, - Mean_value -}; + typedef typename boost::graph_traits::vertex_descriptor TM_vertex_descriptor; + typedef typename boost::graph_traits::vertex_iterator TM_vertex_iterator; + + std::vector cones; + cones.reserve(4); + int cone_index; + while(in >> cone_index) + cones.push_back(cone_index); + + std::cout << "Input cones: "; + for(std::size_t i=0; i 4) { + std::cerr << "Error: Not enough or too many input cones" << std::endl; + return ERROR_WRONG_PARAMETER; + } + + if(!internal::are_cones_unique(cones)) { + std::cerr << "Error: The input cones are not unique" << std::endl; + return ERROR_WRONG_PARAMETER; + } + + // Locate the cones in the underlying mesh 'pm' + std::vector cone_vds_in_tm(cones.size()); // need this to keep the correct order + + // Since the cones are unique, we only need to loop all the vertices once + TM_vertex_iterator vit, end; + boost::tie(vit, end) = vertices(tm); + for(; vit!=end; ++vit) { + for(std::size_t i=0; i +Error_code read_cones(const TriangleMesh& tm, std::ifstream& in, ConeOutputInserter out) +{ + typedef typename boost::graph_traits::vertex_descriptor TM_vertex_descriptor; + typedef typename boost::graph_traits::vertex_iterator TM_vertex_iterator; + + boost::unordered_map m; + int counter = 0; + + TM_vertex_iterator vit, end; + boost::tie(vit, end) = vertices(tm); + for(; vit!=end; ++vit) + { + TM_vertex_descriptor vd = *vit; + m[vd] = counter++; + } + + return read_cones(tm, in, boost::make_assoc_property_map(m), out); +} + +/// \ingroup PkgSurfaceParameterizationOrbifoldHelperFunctions +/// +/// Same as above, but from a file instead of a stream. +template +Error_code read_cones(const TriangleMesh& pm, const char* filename, VertexIndexMap vpmap, ConeOutputInserter out) +{ + std::ifstream in(filename); + return read_cones(pm, in, vpmap, out); +} + +/// \ingroup PkgSurfaceParameterizationOrbifoldHelperFunctions +/// +/// Same as above, but from a file instead of a stream. The default indexation +/// of the vertices of `tm` is used: vertices are numbered from `0` to `num_vertices(tm)-1`, +/// in the order that they appear while calling `vertices(tm)`. +template +Error_code read_cones(const TriangleMesh& pm, const char* filename, ConeOutputInserter out) +{ + std::ifstream in(filename); + return read_cones(pm, in, out); +} + +/// \ingroup PkgSurfaceParameterizationOrbifoldHelperFunctions +/// +/// Locate 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 PkgSurfaceParameterizationEnums Cone_type \endlink). +/// +/// \attention The cones must be ordered: the first and last cones are the extremetities of the seam. +/// +/// \tparam SeamMesh is the same class CGAL::Seam_mesh that is passed to the parameterized. +/// It is a template parameter for convenience (not having to rewrite the template +/// parameters of the class CGAL::Seam_mesh). +/// \tparam ConeInputBidirectionalIterator must be a model of `BidirectionalIterator` +/// 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 PkgSurfaceParameterizationEnums Cone_type \endlink as value type. +/// +/// \param mesh the seam mesh +/// \param first, beyond the range of cones, as vertex descriptors of the base mesh. +/// \param cones a writable property map that will store the cones as vertex descriptors +/// of the seam mesh and associate their respective cone types. +template +bool locate_cones(const SeamMesh& mesh, + ConeInputBidirectionalIterator first, ConeInputBidirectionalIterator beyond, + ConeMap& cones) +{ + typedef typename SeamMesh::TriangleMesh TriangleMesh; + + 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; + const PM_PPM pm_ppmap = get(boost::vertex_point, mesh.mesh()); + + // property map to go from vertex_descriptor to Point_3 + typedef typename internal::Kernel_traits::PPM PPM; + const PPM ppmap = get(boost::vertex_point, mesh); + + BOOST_FOREACH(vertex_descriptor vd, vertices(mesh)) { + for(ConeInputBidirectionalIterator cit=first; cit!=beyond; ++cit) { + ConeInputBidirectionalIterator last = (--beyond)++; + + TM_vertex_descriptor smvd = *cit; + if(get(ppmap, vd) == get(pm_ppmap, smvd)) { // same geometric position + Cone_type ct; + if(cit == first) + ct = First_unique_cone; + else if(cit == last) + ct = Second_unique_cone; + else + ct = Duplicated_cone; + + cones.insert(std::make_pair(vd, ct)); + } + } + } + + return internal::check_cone_validity(mesh, first, beyond, cones); +} + +/// \ingroup PkgSurfaceParameterizationOrbifoldHelperFunctions +/// +/// Same as above, but the cones are not ordered and we thus use seam mesh +/// information to determine which cones are extremities of the seam (so-called +/// unique cones) or not (so-called duplicate cones). +template +bool locate_unordered_cones(const SeamMesh& mesh, + ConeInputBidirectionalIterator first, ConeInputBidirectionalIterator beyond, + ConeMap& cones) +{ + CGAL_precondition(cones.empty()); + CGAL_precondition(std::distance(first, beyond) == 3 || std::distance(first, beyond) == 4); + + typedef typename SeamMesh::TriangleMesh TriangleMesh; + + 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; + + // find a vertex on the seam + vertex_descriptor vertex_on_seam; + BOOST_FOREACH(vertex_descriptor vd, vertices(mesh)) { + if(mesh.has_on_seam(vd)) { + vertex_on_seam = vd; + break; + } + } + + 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; + const PM_PPM pm_ppmap = get(boost::vertex_point, mesh.mesh()); + + // property map to go from vertex_descriptor to Point_3 + typedef typename internal::Kernel_traits::PPM PPM; + const PPM ppmap = get(boost::vertex_point, mesh); + + bool first_cone_met = false; + + // walk on the seam and mark if we encounter a cone + vertex_descriptor end = vertex_on_seam; + do { + ConeInputBidirectionalIterator current = first; + for(; current!=beyond; ++current) { + TM_vertex_descriptor smvd = *current; + if(get(ppmap, vertex_on_seam) == get(pm_ppmap, smvd)) { // the seam mesh vertex is a cone + // We have encountered a cone. Must check if the cone is a Unique_cone + // or a Duplicated_cone. + + // A check is to look at the sources of two halfedges with the same direction + // on either side of the seam. If the sources are the same, it's a Unique_cone; + // if they differ, it's a duplicated_cone. + + halfedge_descriptor hd = halfedge(vertex_on_seam, mesh); + halfedge_descriptor other_hd = opposite(hd, mesh); + + // little trick to go from border halfedge on one side of the seam to + // interior halfedge on the other side of the seam + CGAL_assertion(other_hd.seam); + other_hd.seam = false; + + Cone_type ct; + if(target(hd, mesh) == source(other_hd, mesh)) { + if(first_cone_met) + ct = Second_unique_cone; + else { + ct = First_unique_cone; + first_cone_met = true; + } + } else { + ct = Duplicated_cone; + } + + cones.insert(std::make_pair(vertex_on_seam, ct)); + } + } + + // Move to the next vertex_descriptor on the seam + vertex_on_seam = source(halfedge(vertex_on_seam, mesh), mesh); + CGAL_assertion(mesh.has_on_seam(vertex_on_seam)); + + } while(vertex_on_seam != end); + + return internal::check_cone_validity(mesh, first, beyond, cones); +} /// \ingroup PkgSurfaceParameterizationMethods /// @@ -91,6 +349,10 @@ enum Weight_type /// and a set of vertices of the mesh (the cones). The choice of cones influences /// the resulting parameterization, but not the choice of the seam path between these cones. /// +/// Some helper functions related to the class `Orbifold_Tutte_parameterizer_3` +/// (for example to read and compute paths between cones) can be found +/// \link PkgSurfaceParameterizationOrbifoldHelperFunctions here \endlink. +/// /// The example \ref Surface_mesh_parameterization/orbifold.cpp "orbifold.cpp" /// shows how to select cones on the input mesh and automatically construct /// the seams and the cones on the `Seam_mesh`. @@ -336,11 +598,11 @@ private: // positions of the cones in the plane typedef std::vector Point_container; const Point_container& tcoords = - get_cones_parameterized_coordinates(orb_type); + internal::get_cones_parameterized_coordinates(orb_type); // angles at the cones typedef std::vector Angle_container; - const Angle_container& angs = get_angles_at_cones(orb_type); + const Angle_container& angs = internal::get_angles_at_cones(orb_type); // The index of the line in M that we are filling next. @@ -631,6 +893,9 @@ public: /// 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 ConeMap must be a model of `AssociativeContainer` with key type + /// `boost::graph_traits::%vertex_descriptor` and + /// \link PkgSurfaceParameterizationEnums Cone_type \endlink as value type. /// \tparam VertexUVmap must be a model of `ReadWritePropertyMap` with /// `boost::graph_traits::%vertex_descriptor` as key type and /// %Point_2 (type deduced from `Seam_mesh` using `Kernel_traits`) @@ -638,14 +903,12 @@ public: /// \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 mesh a `Seam_mesh` parameterized by any model of a `FaceGraph` /// \param bhd a halfedge on the border of the seam mesh /// \param cmap a mapping of the `vertex_descriptor`s of `mesh` that are cones - /// to their respective `Cone_type`. + /// to their respective \link PkgSurfaceParameterizationEnums Cone_type \endlink + /// classification. /// \param uvmap an instanciation of the class `VertexUVmap`. /// \param vimap an instanciation of the class `VertexIndexMap`. /// @@ -661,7 +924,7 @@ public: /// or intersect other paths (see Figure below). /// /// \cgalFigureBegin{Surface_mesh_parameterizationfigorbifold, orbifold_path.svg} - /// Bad (left) and good (right) seam paths. The seam edges are shown in dark red. + /// Invalid (left) and valid (right) seam paths. Seam edges are drawn in teal. /// Cones are marked in yellow and blue. /// \cgalFigureEnd template Error_code parameterize(SeamMesh& mesh, halfedge_descriptor bhd, - ConeMap cmap, + const ConeMap& cmap, VertexUVMap uvmap, VertexIndexMap vimap) const { @@ -752,7 +1015,8 @@ public: } public: - /// Constructor. + /// Constructor of the parameterizer. The arguments allow to select + /// the desired orbifold and weight types. Orbifold_Tutte_parameterizer_3(const Orbifold_type orb_type = Square, const Weight_type weight_type = Cotangent) : diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/internal/orbifold_cone_helper.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/internal/orbifold_cone_helper.h index a58db9639b7..513b9128d51 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/internal/orbifold_cone_helper.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/internal/orbifold_cone_helper.h @@ -21,6 +21,7 @@ #include #include +#include #include @@ -41,49 +42,7 @@ namespace CGAL { namespace Surface_mesh_parameterization { -/// \ingroup PkgSurfaceParameterizationEnums -/// -/// The types of cones used in Orbifold Tutte parameterization. -/// -/// `Unique_cones` are found at the beginning and the end of the seam. All other -/// cones are duplicated in the sense that when the seam is `opened`, the vertex -/// is duplicated at two different positions. -enum Cone_type -{ - First_unique_cone = 0, - Second_unique_cone, - Duplicated_cone -}; - -/// \ingroup PkgSurfaceParameterizationEnums -/// -/// The four Orbifold types available in the Orbifold Tutte parameterization. -enum Orbifold_type -{ - Square = 0, - Diamond, - Triangle, - Parallelogram -}; - -/// Get message corresponding to an error code -/// \param orb_type The integer value in the enum -/// \return The string describing the Orbifold type -const char* get_orbifold_type(int orb_type) -{ - // Messages corresponding to Error_code list above. Must be kept in sync! - static const char* type[Parallelogram+1] = { - "Square", - "Diamond", - "Triangle", - "Parallelogram" - }; - - if(orb_type > Parallelogram || orb_type < 0) - return "Unknown orbifold type"; - else - return type[orb_type]; -} +namespace internal { // Orbifold type functions template @@ -130,8 +89,6 @@ NT_container get_angles_at_cones(const Orbifold_type orb_type) return angs; } -namespace internal { - template bool are_cones_unique(const Cone_container& cones) { @@ -198,21 +155,24 @@ void find_start_cone(const ConeMap& cmap, /// Check the validity of the input cones in the `Seam_mesh` mesh. template -bool check_input_validity(const SeamMesh& mesh, - const Cones_in_Seam_mesh_map& cones, - const Cones_in_Base_mesh_container& cone_tm_vds) + typename ConeInputBidirectionalIterator, + typename Cones_in_Seam_mesh_map> +bool check_cone_validity(const SeamMesh& mesh, + ConeInputBidirectionalIterator first, ConeInputBidirectionalIterator beyond, + const Cones_in_Seam_mesh_map& cones) { typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; typedef typename boost::graph_traits::vertex_descriptor TM_vertex_descriptor; + typename std::iterator_traits< + ConeInputBidirectionalIterator>::difference_type number_of_cones_in_tm = std::distance(first, beyond); + // check cone numbers - if((cone_tm_vds.size() == 3 && cones.size() != 4) || - (cone_tm_vds.size() == 4 && cones.size() != 6)) { + if((number_of_cones_in_tm == 3 && cones.size() != 4) || + (number_of_cones_in_tm == 4 && cones.size() != 6)) { std::cerr << "Error: Problem in number of cones: " << std::endl; - std::cerr << cone_tm_vds.size() << " cones in the base mesh" << std::endl; + std::cerr << number_of_cones_in_tm << " cones in the base mesh" << std::endl; std::cerr << cones.size() << " cones in the seam mesh" << std::endl; return false; } @@ -251,8 +211,8 @@ bool check_input_validity(const SeamMesh& mesh, return false; } - if((cone_tm_vds.size() == 3 && duplicated_cone_counter != 2) || - (cone_tm_vds.size() == 4 && duplicated_cone_counter != 4)) { + if((number_of_cones_in_tm == 3 && duplicated_cone_counter != 2) || + (number_of_cones_in_tm == 4 && duplicated_cone_counter != 4)) { std::cerr << "Error: Wrong number of duplicated cones" << std::endl; return false; } @@ -332,207 +292,6 @@ bool check_input_validity(const SeamMesh& mesh, return true; } -/// Read the cones from an input stream. -template -Error_code read_cones(const TriangleMesh& pm, std::ifstream& in, - std::vector::vertex_descriptor>& cone_vds_in_tm) -{ - typedef typename boost::graph_traits::vertex_descriptor TM_vertex_descriptor; - - std::vector cones; - cones.reserve(4); - int cone_index; - while(in >> cone_index) { - cones.push_back(cone_index); - } - - std::cout << "Input cones: "; - for(std::size_t i=0; i 4) { - std::cerr << "Error: Not enough or too many input cones" << std::endl; - return ERROR_WRONG_PARAMETER; - } - - if(!are_cones_unique(cones)) { - std::cerr << "Error: The input cones are not unique" << std::endl; - return ERROR_WRONG_PARAMETER; - } - - // Locate the cones in the underlying mesh 'pm' - CGAL_assertion(cone_vds_in_tm.empty()); - cone_vds_in_tm.resize(cones.size()); - - for(std::size_t i=0; i -Error_code read_cones(const TriangleMesh& pm, const char* filename, - std::vector::vertex_descriptor>& cone_vds_in_tm) -{ - std::ifstream in(filename); - return read_cones(pm, in, cone_vds_in_tm); -} - - -/// Locate the cones on the seam mesh (find the corresponding seam mesh -/// vertex_descriptor) and mark them with a tag that indicates whether it is a -/// simple cone or a duplicated cone. -/// -/// The cones are ordered: the first and last cones are the extremetities of the seam. -/// -/// \tparam SeamMesh is a seam mesh -/// \tparam ConeMap a map vertex_descriptor --> Cone_type -template -bool locate_cones(const SeamMesh& mesh, - const Cones_in_pmesh_vector& cone_tm_vds, - ConeMap& cones) -{ - CGAL_precondition(cones.empty()); - - typedef typename SeamMesh::TriangleMesh TriangleMesh; - - 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 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 - typedef typename Kernel_traits::PPM PPM; - const PPM ppmap = get(boost::vertex_point, mesh); - - // the cones in the underlying mesh - std::size_t cvdss = cone_tm_vds.size(); - - for(std::size_t i=0; i Cone_type -template -bool locate_unordered_cones(const SeamMesh& mesh, - const Cones_in_pmesh_set& cone_tm_vds, - ConeMap& cones) -{ - CGAL_precondition(cones.empty()); - CGAL_precondition(cone_tm_vds.size() == 3 || cone_tm_vds.size() == 4); - - typedef typename SeamMesh::TriangleMesh TriangleMesh; - - 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; - - // find a vertex on the seam - vertex_descriptor vertex_on_seam; - BOOST_FOREACH(vertex_descriptor vd, vertices(mesh)) { - if(mesh.has_on_seam(vd)) { - vertex_on_seam = vd; - break; - } - } - - CGAL_assertion(vertex_on_seam != vertex_descriptor()); - - // property map to go from TM_vertex_descriptor to Point_3 - typedef typename 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 - typedef typename Kernel_traits::PPM PPM; - const PPM ppmap = get(boost::vertex_point, mesh); - - bool first_cone_met = false; - - // walk on the seam and mark if we encounter a cone - vertex_descriptor end = vertex_on_seam; - do { - BOOST_FOREACH(TM_vertex_descriptor smvd, cone_tm_vds) { - if(get(ppmap, vertex_on_seam) == get(pm_ppmap, smvd)) { // the seam mesh vertex is a cone - - // we have encountered a cone. Must check if the cone is a Unique_cone - // or a Duplicated_cone. - - // a check is to look at the sources of two halfedges with the same direction - // on either side of the seam. If the sources are the same, it's a Unique_cone; - // if they differ, it's a duplicated_cone. - - halfedge_descriptor hd = halfedge(vertex_on_seam, mesh); - halfedge_descriptor other_hd = opposite(hd, mesh); - - // little trick to go from border halfedge on one side of the seam to - // interior halfedge on the other side of the seam - CGAL_assertion(other_hd.seam); - other_hd.seam = false; - - Cone_type ct; - if(target(hd, mesh) == source(other_hd, mesh)) { - if(first_cone_met) - ct = Second_unique_cone; - else { - ct = First_unique_cone; - first_cone_met = true; - } - } else { - ct = Duplicated_cone; - } - - cones.insert(std::make_pair(vertex_on_seam, ct)); - } - } - - // move to the next vertex_descriptor on the seam - vertex_on_seam = source(halfedge(vertex_on_seam, mesh), mesh); - CGAL_assertion(mesh.has_on_seam(vertex_on_seam)); - - } while(vertex_on_seam != end); - - return check_input_validity(mesh, cones, cone_tm_vds); -} - } // namespace internal } // namespace Surface_mesh_parameterization diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/orbifold_enums.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/orbifold_enums.h new file mode 100644 index 00000000000..348e8eef43c --- /dev/null +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/orbifold_enums.h @@ -0,0 +1,87 @@ +// Copyright (c) 2018 GeometryFactory (France). +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org). +// You can redistribute it and/or modify it under the terms of the GNU +// General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// Author(s) : Mael Rouxel-Labbé + +#ifndef CGAL_SURFACE_MESH_PARAMETERIZATION_ORBIFOLD_ENUMS_H +#define CGAL_SURFACE_MESH_PARAMETERIZATION_ORBIFOLD_ENUMS_H + +#include + +/// \file orbifold_enums.h + +namespace CGAL { + +namespace Surface_mesh_parameterization { + +/// \ingroup PkgSurfaceParameterizationEnums +/// +/// The two possible weight types available in the Orbifold Tutte parameterization. +enum Weight_type +{ + Cotangent = 0, ///< When Cotangent weights are used, the orbifold-Tutte embedding + /// globally minimizes the Dirichlet energy and approximates conformal mappings. + Mean_value ///< Mean Value Coordinate weights are guaranteed to generate positive edge weights, + /// and the parameterization is guaranteed to be injective. +}; + +/// \ingroup PkgSurfaceParameterizationEnums +/// +/// A classification type for the cones used in Orbifold Tutte parameterization. +enum Cone_type +{ + First_unique_cone = 0, ///< marker for the cone found at the beginning of the seam. + Second_unique_cone, ///< marker for the cone found at the end of the seam. + Duplicated_cone ///< marker for all the other cones. Cones are duplicated in the sense + /// that when the seam is "opened", the cone appears + /// at two different positions. +}; + +/// \ingroup PkgSurfaceParameterizationEnums +/// +/// The four Orbifold types available in the Orbifold Tutte parameterization. +/// The different shapes result from the number of cones and the angle constraints +/// at the cones. +enum Orbifold_type +{ + Square = 0, ///< Three cones, forming a square-shaped basic tile. + Diamond, ///< Three cones, forming a diamond-shaped basic tile. + Triangle, ///< Three cones, forming a triangle-shaped basic tile. + Parallelogram ///< Four cones, forming a parallelogram-shaped basic tile. +}; + +/// \ingroup PkgSurfaceParameterizationEnums +/// \brief Convert the orbifold type to a literal message. +/// \param orb_type the integer value in the enum +/// \return the string describing the Orbifold type. +const char* get_orbifold_type(int orb_type) +{ + // Messages corresponding to the different orbifold types. + static const char* type[Parallelogram+1] = { + "Square", + "Diamond", + "Triangle", + "Parallelogram" + }; + + if(orb_type > Parallelogram || orb_type < 0) + return "Unknown orbifold type"; + else + return type[orb_type]; +} + +} // namespace Surface_mesh_parameterization +} // namespace CGAL + +#endif diff --git a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/internal/shortest_path.h b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/orbifold_shortest_path.h similarity index 61% rename from Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/internal/shortest_path.h rename to Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/orbifold_shortest_path.h index 4514a085fdc..221a9a49ecc 100644 --- a/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/internal/shortest_path.h +++ b/Surface_mesh_parameterization/include/CGAL/Surface_mesh_parameterization/orbifold_shortest_path.h @@ -12,10 +12,14 @@ // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // +// $URL$ +// $Id$ +// SPDX-License-Identifier: GPL-3.0+ +// // Author(s) : Mael Rouxel-Labbé -#ifndef CGAL_SURFACE_MESH_PARAMETERIZATION_INTERNAL_SHORTEST_PATH_H -#define CGAL_SURFACE_MESH_PARAMETERIZATION_INTERNAL_SHORTEST_PATH_H +#ifndef CGAL_SURFACE_MESH_PARAMETERIZATION_SHORTEST_PATH_H +#define CGAL_SURFACE_MESH_PARAMETERIZATION_SHORTEST_PATH_H #include @@ -97,11 +101,28 @@ public: } }; -template +} // namespace internal + +/// \ingroup PkgSurfaceParameterizationOrbifoldHelperFunctions +/// +/// Compute the shortest path between `source` and `target` over `mesh`, using +/// a Dijkstra algorithm. +/// +/// \tparam TriangleMesh A triangle mesh, model of `FaceListGraph` and `HalfedgeListGraph`. +/// \tparam EdgeOutputIterator A model of `OutputIterator` with value type +/// `boost::graph_traits::edge_descriptor`. +/// +/// \param tm the triangular mesh to be parameterized +/// \param source, target the extremities of the path to be computed +/// \param oi the output iterator +/// +/// \pre `source` and `target` are vertices of `mesh`. +/// \pre `source != target`. +template void compute_shortest_paths_between_two_cones(const TriangleMesh& mesh, - typename boost::graph_traits::vertex_descriptor source, - typename boost::graph_traits::vertex_descriptor target, - OutputIterator oi) + typename boost::graph_traits::vertex_descriptor source, + typename boost::graph_traits::vertex_descriptor target, + EdgeOutputIterator oi) { if(source == target) { std::cout << "Warning: the source and target are identical in 'shortest_path' " << std::endl; @@ -111,7 +132,7 @@ void compute_shortest_paths_between_two_cones(const TriangleMesh& mesh, typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; typedef typename boost::graph_traits::edge_descriptor edge_descriptor; - typedef Stop_at_target_Dijkstra_visitor Stop_visitor; + typedef internal::Stop_at_target_Dijkstra_visitor Stop_visitor; typedef boost::unordered_map Pred_umap; typedef boost::associative_property_map Pred_pmap; @@ -140,25 +161,42 @@ void compute_shortest_paths_between_two_cones(const TriangleMesh& mesh, } while (s != source); } -template +/// \ingroup PkgSurfaceParameterizationOrbifoldHelperFunctions +/// +/// Given a range `[first; beyond[` of cones (described as vertex descriptors), +/// compute the shortest path for all pairs of consecutive entries in the range +/// and add them to the container `seams`. +/// +/// \tparam TriangleMesh A triangle mesh, model of `FaceListGraph` and `HalfedgeListGraph`. +/// \tparam InputConesForwardIterator A model of `ForwardIterator` with value type +/// `boost::graph_traits::vertex_descriptor`. +/// \tparam SeamContainer A model of `SequenceContainer` with value type `boost::graph_traits::edge_descriptor`. +/// +/// \param mesh the triangular mesh on which paths are computed +/// \param first, beyond the cones, which form the extremities of the path +/// \param seams a container that will store the paths, as a sequence of edges of the mesh. +/// +/// \pre `source` and `target` are vertices of `mesh`. +/// \pre `source != target`. +template void compute_shortest_paths_between_cones(const TriangleMesh& mesh, - const Cones_vector& cones, - Seam_container& seams) + InputConesForwardIterator first, InputConesForwardIterator beyond, + SeamContainer& seams) { - CGAL_precondition(cones.size() == 3 || cones.size() == 4); - for(std::size_t i=0; i #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 916cad814f3..9ff1649fe82 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 @@ -6,7 +6,6 @@ #include #include -#include #include #include @@ -420,9 +419,8 @@ int main(int, char**) const char* cone_filename = "data/fandisk.orbifold.selection.txt"; // Read the cones and find the corresponding vertex_descriptor in the underlying mesh 'sm' - typedef std::vector Cones_in_smesh_container; - Cones_in_smesh_container cone_sm_vds; - SMP::internal::read_cones(sm, cone_filename, cone_sm_vds); + std::vector cone_sm_vds; + SMP::read_cones(sm, cone_filename, std::back_inserter(cone_sm_vds)); // Two property maps to store the seam edges and vertices SM_seam_edge_pmap seam_edge_pm = sm.add_property_map("e:on_seam", false).first; @@ -435,7 +433,7 @@ int main(int, char**) SM_halfedge_descriptor smhd = mesh.add_seams(cone_filename); if(smhd == SM_halfedge_descriptor() ) { std::list seam_edges; - SMP::internal::compute_shortest_paths_between_cones(sm, cone_sm_vds, seam_edges); + SMP::compute_shortest_paths_between_cones(sm, cone_sm_vds.begin(), cone_sm_vds.end(), seam_edges); // Add the seams to the seam mesh BOOST_FOREACH(SM_edge_descriptor e, seam_edges) { @@ -453,11 +451,8 @@ int main(int, char**) } // Mark the cones in the seam mesh - typedef boost::unordered_map Cones; - Cones cmap; - SMP::internal::locate_cones(mesh, cone_sm_vds, cmap); + boost::unordered_map cmap; + SMP::locate_cones(mesh, cone_sm_vds.begin(), cone_sm_vds.end(), cmap); // The 2D points of the uv parametrisation will be written into this map // Note that this is a halfedge property map, and that uv values