From 9bb0de291165c6accf0b2917811b306747863e12 Mon Sep 17 00:00:00 2001 From: Janis Born Date: Fri, 30 Nov 2018 20:12:27 +0100 Subject: [PATCH 1/6] add HandleToPropHandle metaprogram helper --- src/OpenMesh/Core/Utils/HandleToPropHandle.hh | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 src/OpenMesh/Core/Utils/HandleToPropHandle.hh diff --git a/src/OpenMesh/Core/Utils/HandleToPropHandle.hh b/src/OpenMesh/Core/Utils/HandleToPropHandle.hh new file mode 100644 index 00000000..4332b537 --- /dev/null +++ b/src/OpenMesh/Core/Utils/HandleToPropHandle.hh @@ -0,0 +1,40 @@ +#ifndef HANDLETOPROPHANDLE_HH_ +#define HANDLETOPROPHANDLE_HH_ + +#include +#include + +namespace OpenMesh { + + template + struct HandleToPropHandle { + }; + + template + struct HandleToPropHandle { + using type = OpenMesh::VPropHandleT; + }; + + template + struct HandleToPropHandle { + using type = OpenMesh::HPropHandleT; + }; + + template + struct HandleToPropHandle { + using type = OpenMesh::EPropHandleT; + }; + + template + struct HandleToPropHandle { + using type = OpenMesh::FPropHandleT; + }; + + template + struct HandleToPropHandle { + using type = OpenMesh::MPropHandleT; + }; + +} // namespace OpenMesh + +#endif // HANDLETOPROPHANDLE_HH_ \ No newline at end of file From 7bd56c6fc184e5fac84f0399ab8d7221011e0d9b Mon Sep 17 00:00:00 2001 From: Janis Born Date: Fri, 30 Nov 2018 20:13:05 +0100 Subject: [PATCH 2/6] rename PropertyManager factory functions makePropertyManagerFromNew -> makeTemporaryProperty makePropertyManagerFromExisting -> getProperty makePropertyManagerFromExistingOrNew-> getOrMakeProperty deprecate old function names --- src/OpenMesh/Core/Utils/PropertyManager.hh | 166 +++++++++++++++++---- 1 file changed, 135 insertions(+), 31 deletions(-) diff --git a/src/OpenMesh/Core/Utils/PropertyManager.hh b/src/OpenMesh/Core/Utils/PropertyManager.hh index ae8ad189..7a1c371b 100644 --- a/src/OpenMesh/Core/Utils/PropertyManager.hh +++ b/src/OpenMesh/Core/Utils/PropertyManager.hh @@ -49,6 +49,8 @@ #ifndef PROPERTYMANAGER_HH_ #define PROPERTYMANAGER_HH_ +#include +#include #include #include #include @@ -60,35 +62,23 @@ namespace OpenMesh { * It also defines convenience operators to access the encapsulated * property's value. * - * For C++11, it is recommended to use the factory functions - * makePropertyManagerFromNew, makePropertyManagerFromExisting, - * makePropertyManagerFromExistingOrNew to construct a PropertyManager, e.g. + * It is recommended to use the factory functions + * makeTemporaryProperty(), getProperty(), and getOrMakeProperty() + * to construct a PropertyManager, e.g. * * \code - * TriMesh mesh; - * auto visited = makePropertyManagerFromNew>(mesh, "visited.plugin-example.i8.informatik.rwth-aachen.de"); + * { + * TriMesh mesh; + * auto visited = makeTemporaryProperty(mesh); * - * for (auto vh : mesh.vertices()) { - * if (!visited[vh]) { - * visitComponent(mesh, vh, visited); + * for (auto vh : mesh.vertices()) { + * if (!visited[vh]) { + * visitComponent(mesh, vh, visited); + * } * } + * // The property is automatically removed at the end of the scope * } * \endcode - * - * For C++98, it is usually more convenient to use the constructor explicitly, - * i.e. - * - * \code - * TriMesh mesh; - * PropertyManager, TriMesh> visited(mesh, "visited.plugin-example.i8.informatik.rwth-aachen.de"); - * - * for (TriMesh::VertexIter vh_it = mesh.begin(); ... ; ...) { - * if (!visited[*vh_it]) { - * visitComponent(mesh, *vh_it, visited); - * } - * } - * \endcode - * */ template class PropertyManager { @@ -493,19 +483,127 @@ class PropertyManager { std::string name_; }; -/** \relates PropertyManager +/** @relates PropertyManager + * + * Creates a new property whose lifetime is limited to the current scope. + * + * Used for temporary properties. Shadows any existing properties of + * matching name and type. + * + * Example: + * @code + * PolyMesh m; + * { + * auto is_quad = makeTemporaryProperty(m); + * for (auto& fh : m.faces()) { + * is_quad[fh] = (m.valence(fh) == 4); + * } + * // The property is automatically removed from the mesh at the end of the scope. + * } + * @endcode + * + * @param mesh The mesh on which the property is created + * @param propname (optional) The name of the created property + * @tparam ElementT Element type of the created property, e.g. VertexHandle, HalfedgeHandle, etc. + * @tparam T Value type of the created property, e.g., \p double, \p int, etc. + * @tparam MeshT Type of the mesh. Can often be inferred from \p mesh + * @returns A PropertyManager handling the lifecycle of the property + */ +template +PropertyManager::type, MeshT> +makeTemporaryProperty(MeshT &mesh, const char *propname = "") { + return PropertyManager::type, MeshT>(mesh, propname, false); +} + +/** @relates PropertyManager + * + * Obtains a handle to a named property. + * + * Example: + * @code + * PolyMesh m; + * { + * try { + * auto is_quad = getProperty(m, "is_quad"); + * // Use is_quad here. + * } + * catch (const std::runtime_error& e) { + * // There is no is_quad face property on the mesh. + * } + * } + * @endcode + * + * @pre Property with the name \p propname of matching type exists. + * @throws std::runtime_error if no property with the name \p propname of + * matching type exists. + * @param mesh The mesh on which the property is created + * @param propname The name of the created property + * @tparam ElementT Element type of the created property, e.g. VertexHandle, HalfedgeHandle, etc. + * @tparam T Value type of the created property, e.g., \p double, \p int, etc. + * @tparam MeshT Type of the mesh. Can often be inferred from \p mesh + * @returns A PropertyManager wrapping the property + */ +template +PropertyManager::type, MeshT> +getProperty(MeshT &mesh, const char *propname) { + return PropertyManager::type, MeshT>(mesh, propname, true); +} + +/** @relates PropertyManager + * + * Obtains a handle to a named property if it exists or creates a new one otherwise. + * + * Used for creating or accessing permanent properties. + * + * Example: + * @code + * PolyMesh m; + * { + * auto is_quad = getOrMakeProperty(m, "is_quad"); + * for (auto& fh : m.faces()) { + * is_quad[fh] = (m.valence(fh) == 4); + * } + * // The property remains on the mesh after the end of the scope. + * } + * { + * // Retrieve the property from the previous scope. + * auto is_quad = getOrMakeProperty(m, "is_quad"); + * // Use is_quad here. + * } + * @endcode + * + * @param mesh The mesh on which the property is created + * @param propname The name of the created property + * @tparam ElementT Element type of the created property, e.g. VertexHandle, HalfedgeHandle, etc. + * @tparam T Value type of the created property, e.g., \p double, \p int, etc. + * @tparam MeshT Type of the mesh. Can often be inferred from \p mesh + * @returns A PropertyManager wrapping the property + */ +template +PropertyManager::type, MeshT> +getOrMakeProperty(MeshT &mesh, const char *propname) { + return PropertyManager::type, MeshT>::createIfNotExists(mesh, propname); +} + +/** @relates PropertyManager + * @deprecated Use makeTemporaryProperty() instead. + * * Creates a new property whose lifecycle is managed by the returned * PropertyManager. * - * Intended for temporary properties. Shadows any existsing properties of + * Intended for temporary properties. Shadows any existing properties of * matching name and type. */ template -PropertyManager makePropertyManagerFromNew(MeshT &mesh, const char *propname) { +OM_DEPRECATED("Use makeTemporaryProperty instead.") +PropertyManager makePropertyManagerFromNew(MeshT &mesh, const char *propname) +{ return PropertyManager(mesh, propname, false); } /** \relates PropertyManager + * @deprecated Use getProperty() instead. + * * Creates a non-owning wrapper for an existing mesh property (no lifecycle * management). * @@ -516,22 +614,28 @@ PropertyManager makePropertyManagerFromNew(MeshT &mesh, const c * matching type exists. */ template -PropertyManager makePropertyManagerFromExisting(MeshT &mesh, const char *propname) { +OM_DEPRECATED("Use getProperty instead.") +PropertyManager makePropertyManagerFromExisting(MeshT &mesh, const char *propname) +{ return PropertyManager(mesh, propname, true); } -/** \relates PropertyManager +/** @relates PropertyManager + * @deprecated Use getOrMakeProperty() instead. + * * Creates a non-owning wrapper for a mesh property (no lifecycle management). * If the given property does not exist, it is created. * * Intended for creating or accessing persistent properties. */ template -PropertyManager makePropertyManagerFromExistingOrNew(MeshT &mesh, const char *propname) { +OM_DEPRECATED("Use getOrMakeProperty instead.") +PropertyManager makePropertyManagerFromExistingOrNew(MeshT &mesh, const char *propname) +{ return PropertyManager::createIfNotExists(mesh, propname); } -/** \relates PropertyManager +/** @relates PropertyManager * Like the two parameter version of makePropertyManagerFromExistingOrNew() * except it initializes the property with the specified value over the * specified range if it needs to be created. If the property already exists, @@ -552,7 +656,7 @@ PropertyManager makePropertyManagerFromExistingOrNew( mesh, propname, begin, end, init_value); } -/** \relates PropertyManager +/** @relates PropertyManager * Like the two parameter version of makePropertyManagerFromExistingOrNew() * except it initializes the property with the specified value over the * specified range if it needs to be created. If the property already exists, From 82708a3533fa6172e42b585f91a69b5a3221950f Mon Sep 17 00:00:00 2001 From: Janis Born Date: Fri, 30 Nov 2018 20:13:37 +0100 Subject: [PATCH 3/6] update custom properties tutorial to use PropertyManager and the new factory functions --- Doc/Tutorial/03-properties/smooth.cc | 114 ++++++++++++--------------- Doc/tutorial_03.docu | 83 ++++++++++++------- 2 files changed, 103 insertions(+), 94 deletions(-) diff --git a/Doc/Tutorial/03-properties/smooth.cc b/Doc/Tutorial/03-properties/smooth.cc index 8756c492..11866db7 100644 --- a/Doc/Tutorial/03-properties/smooth.cc +++ b/Doc/Tutorial/03-properties/smooth.cc @@ -1,74 +1,58 @@ -#include -#include -// -------------------- #include #include +#include -typedef OpenMesh::TriMesh_ArrayKernelT<> MyMesh; +#include +#include +using MyMesh = OpenMesh::TriMesh_ArrayKernelT<>; -int main(int argc, char **argv) +int main(int argc, char** argv) { - MyMesh mesh; - - - // check command line options - if (argc != 4) - { - std::cerr << "Usage: " << argv[0] << " #iterations infile outfile\n"; - return 1; - } - - - - // read mesh from stdin - if ( ! OpenMesh::IO::read_mesh(mesh, argv[2]) ) - { - std::cerr << "Error: Cannot read mesh from " << argv[2] << std::endl; - return 1; - } - - - - // this vertex property stores the computed centers of gravity - OpenMesh::VPropHandleT cogs; - mesh.add_property(cogs); - - // smoothing mesh argv[1] times - MyMesh::VertexIter v_it, v_end(mesh.vertices_end()); - MyMesh::VertexVertexIter vv_it; - MyMesh::Point cog; - MyMesh::Scalar valence; - unsigned int i, N(atoi(argv[1])); - - - for (i=0; i < N; ++i) - { - for (v_it=mesh.vertices_begin(); v_it!=v_end; ++v_it) - { - mesh.property(cogs,*v_it).vectorize(0.0f); - valence = 0.0; - - for (vv_it=mesh.vv_iter( *v_it ); vv_it; ++vv_it) - { - mesh.property(cogs,*v_it) += mesh.point( *vv_it ); - ++valence; - } - mesh.property(cogs,*v_it) /= valence; + // Read command line options + MyMesh mesh; + if (argc != 4) { + std::cerr << "Usage: " << argv[0] << " #iterations infile outfile" << std::endl; + return 1; + } + const int iterations = argv[1]; + const std::string infile = argv[2]; + const std::string outfile = argv[3]; + + // Read mesh file + if (!OpenMesh::IO::read_mesh(mesh, infile)) { + std::cerr << "Error: Cannot read mesh from " << infile << std::endl; + return 1; } - for (v_it=mesh.vertices_begin(); v_it!=v_end; ++v_it) - if ( !mesh.is_boundary( *v_it ) ) - mesh.set_point( *v_it, mesh.property(cogs,*v_it) ); - } - - - // write mesh to stdout - if ( ! OpenMesh::IO::write_mesh(mesh, argv[3]) ) - { - std::cerr << "Error: cannot write mesh to " << argv[3] << std::endl; - return 1; - } - - return 0; + { + // Add a vertex property storing the computed centers of gravity + auto cog = OpenMesh::makeTemporaryProperty(mesh); + + // Smooth the mesh several times + for (int i = 0; i < iterations; ++i) { + // Iterate over all vertices to compute centers of gravity + for (const auto& vh : mesh.vertices()) { + cog[vv] = {0,0,0}; + int valence = 0; + // Iterate over all 1-ring vertices around vh + for (const auto& vvh : mesh.vv_range(vh)) { + cog[vv] += mesh.point(vvh); + ++valence; + } + cog[vv] /= valence; + } + // Move all vertices to the previously computed positions + for (const auto& vh : mesh.vertices()) { + mesh.point(vv) = cog[vv]; + } + } + // The cog vertex property is removed from the mesh at the end of this scope + } + + // Write mesh file + if (!OpenMesh::IO::read_mesh(mesh, outfile)) { + std::cerr << "Error: Cannot write mesh to " << outfile << std::endl; + return 1; + } } diff --git a/Doc/tutorial_03.docu b/Doc/tutorial_03.docu index 7d8fb228..9333b210 100644 --- a/Doc/tutorial_03.docu +++ b/Doc/tutorial_03.docu @@ -1,7 +1,7 @@ /** \page tutorial_03 Using (custom) properties This examples shows: -- How to add and remove custom properties, +- How to add and remove custom properties - How to get and set the value of a custom property In the last example we computed the barycenter of each vertex' @@ -11,44 +11,69 @@ let %OpenMesh manage the data. It would be even more helpful if we could attach such properties dynamically to the mesh. -%OpenMesh provides dynamic properties, which can be attached to each -mesh entity (vertex, face, edge, halfedge, and the mesh itself). We -distinguish between custom and standard properties. A custom property -is any user-defined property and is accessed via the member function -\c property(..) via a handle and an entity handle -(e.g. VertexHandle). Whereas the standard properties are accessed via -special member functions, e.g. the vertex position is accessed with \c -point(..) and a vertex handle. +Custom properties can be conveniently created and attached to meshes with the following functions: +- makeTemporaryProperty() creates a property that is temporary to the current scope. +- getOrMakeProperty() is used for creating and accessing permanent named properties on a mesh. +- getProperty() is used for accessing an existing permanent named property on a mesh. -In this example we will store the \c cog-value (see previous example) -in an additional vertex property instead of keeping it in a separate -array. To do so we define first a so-called property handle with the desired -type (\c MyMesh::Point) and register the handle at the mesh: +All three functions take two template arguments: +- First, the type of the mesh element that the property is attached to (i.e. OpenMesh::VertexHandle, OpenMesh::HalfedgeHandle, OpenMesh::EdgeHandle, or OpenMesh::FaceHandle). +- Second, the type of the property value that is attached to each element (e.g., \p int, \p double, etc.). + +All three functions return a handle object (of type PropertyManager) that manages the lifetime of the property and provides read / write access to its values. + +In this example, we will store the \c cog value (see previous example) in a vertex property instead of keeping it in a separate array. +To do so, we first add a (temporary) property of the desired element type (OpenMesh::VertexHandle) and value type (\c %MyMesh::Point) to the mesh: \dontinclude 03-properties/smooth.cc -\skipline vertex property stores -\until mesh.add +\skipline makeTemporaryProperty -
The \c mesh allocates enough memory to hold as many elements of type -\c MyMesh::Point as number of vertices exist, and of course the mesh -synchronizes all insert and delete operations on the vertices with the -vertex properties. +Enough memory is allocated to hold as many values of \c %MyMesh::Point as there are vertices. +All insert and delete operations on the mesh are synchronized with the attached properties. -Once the wanted property is registered we can use the property to -calculate the barycenter of the neighborhood of each vertex \c v_it +Once the property is created, we can use it to compute the centers of the neighborhood of each vertex: -\dontinclude 03-properties/smooth.cc -\skipline vv_it= +\skipline mesh.vertices +\until cog[vv] /= valence \until } -\until mesh.prop -
and finally set the new position for each vertex \c v_it +Finally, we set the new position for each vertex: -\dontinclude 03-properties/smooth.cc -\skipline mesh.set_point +\skipline mesh.vertices +\until mesh.point +\until } -
Below is the complete source code: +--- + +Since we chose to use makeTemporaryProperty(), the created property is automatically removed from the mesh as soon as we leave the scope of the associated handle variable \c cog. + +If, instead, a property is desired to survive its local scope, it should be created with using getOrMakeProperty(). In that case, the property must be given a name that can later be used to retrieve the property. For example: + +\code + auto face_area = OpenMesh::makeTemporaryProperty(mesh, 'face_area'); +\endcode + +At a later time, we can use the getProperty() function to obtain a handle to a property that was previously created by refering to its name: +\code + try { + auto face_area = OpenMesh::getProperty(mesh, 'face_area'); + // Use the face_area property. + } + catch (const std::runtime_error& e) { + // Property not found. Handle the error here. + } +\endcode + +--- + +The functions makeTemporaryProperty(), getOrMakeProperty(), and getProperty() are the convenient high-level interface for creating and accessing mesh properties. + +Beneath these convenience functions, there is also a low-level property interface where handle and property lifetime must be managed manually. This interface is accessed through a mesh's add_property(), remove_property(), and property() functions and several property handle classes (OpenMesh::VPropHandleT, OpenMesh::HPropHandleT, OpenMesh::EPropHandleT, OpenMesh::FPropHandleT, OpenMesh::MPropHandleT). + +--- + +Below is the complete source code: \include 03-properties/smooth.cc -*/ \ No newline at end of file +*/ From c5d8ea8ef0e981cc2b94b12dde8af050e7bc98d0 Mon Sep 17 00:00:00 2001 From: Janis Born Date: Fri, 30 Nov 2018 20:14:17 +0100 Subject: [PATCH 4/6] update PropertyManager unit tests to use new factory functions --- src/Unittests/unittests_propertymanager.cc | 24 ++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/Unittests/unittests_propertymanager.cc b/src/Unittests/unittests_propertymanager.cc index 3fd7b646..2fe59654 100644 --- a/src/Unittests/unittests_propertymanager.cc +++ b/src/Unittests/unittests_propertymanager.cc @@ -93,7 +93,6 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) { ASSERT_TRUE(pm_f_bool[*f_it]); } -#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__) /* * Same thing again, this time with C++11 ranges. */ @@ -129,15 +128,13 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) { f_it != f_end; ++f_it) ASSERT_TRUE(pm_f_bool[*f_it]); } -#endif } /* * ==================================================================== - * C++11 Specific Tests + * Factory Functions * ==================================================================== */ -#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__) template bool has_property(const Mesh& _mesh, const std::string& _name) { @@ -154,7 +151,7 @@ TEST_F(OpenMeshPropertyManager, cpp11_temp_property) { ASSERT_FALSE(has_property(mesh_, prop_name)); { - auto vprop = OpenMesh::makePropertyManagerFromNew(mesh_, prop_name); + auto vprop = OpenMesh::makeTemporaryProperty(mesh_, prop_name); static_cast(vprop); // Unused variable ASSERT_TRUE(has_property(mesh_, prop_name)); } @@ -172,13 +169,13 @@ TEST_F(OpenMeshPropertyManager, cpp11_temp_property_shadowing) { using handle_type = OpenMesh::VPropHandleT; const auto prop_name = "pm_v_test_property"; - auto outer_prop = OpenMesh::makePropertyManagerFromNew(mesh_, prop_name); + auto outer_prop = OpenMesh::makeTemporaryProperty(mesh_, prop_name); outer_prop[vh] = 100; ASSERT_EQ(100, outer_prop[vh]); { // inner_prop uses same type and name as outer_prop - auto inner_prop = OpenMesh::makePropertyManagerFromNew(mesh_, prop_name); + auto inner_prop = OpenMesh::makeTemporaryProperty(mesh_, prop_name); inner_prop[vh] = 200; ASSERT_EQ(200, inner_prop[vh]); // End of scope: inner_prop is removed from mesh_ @@ -205,7 +202,7 @@ TEST_F(OpenMeshPropertyManager, cpp11_persistent_and_non_owning_properties) { ASSERT_FALSE(has_property(mesh_, prop_name)); { - auto prop = OpenMesh::makePropertyManagerFromExistingOrNew(mesh_, prop_name); + auto prop = OpenMesh::getOrMakeProperty(mesh_, prop_name); prop[vh] = 100; // End of scope, property persists } @@ -214,7 +211,7 @@ TEST_F(OpenMeshPropertyManager, cpp11_persistent_and_non_owning_properties) { { // Since a property of the same name and type already exists, this refers to the existing property. - auto prop = OpenMesh::makePropertyManagerFromExistingOrNew(mesh_, prop_name); + auto prop = OpenMesh::getOrMakeProperty(mesh_, prop_name); ASSERT_EQ(100, prop[vh]); prop[vh] = 200; // End of scope, property persists @@ -224,7 +221,7 @@ TEST_F(OpenMeshPropertyManager, cpp11_persistent_and_non_owning_properties) { { // Acquire non-owning handle to the property, knowing it exists - auto prop = OpenMesh::makePropertyManagerFromExisting(mesh_, prop_name); + auto prop = OpenMesh::getProperty(mesh_, prop_name); ASSERT_EQ(200, prop[vh]); } @@ -232,12 +229,13 @@ TEST_F(OpenMeshPropertyManager, cpp11_persistent_and_non_owning_properties) { { // Attempt to acquire non-owning handle for a non-existing property - ASSERT_THROW(OpenMesh::makePropertyManagerFromExisting(mesh_, "wrong_property_name"), std::runtime_error); + auto code_that_throws = [&](){ + OpenMesh::getProperty(mesh_, "wrong_prop_name"); + }; + ASSERT_THROW(code_that_throws(), std::runtime_error); } ASSERT_TRUE(has_property(mesh_, prop_name)); } -#endif - } From 6578d0da9cdb0a3335d311ebae80bc2c6a562f5d Mon Sep 17 00:00:00 2001 From: Janis Born Date: Sat, 1 Dec 2018 12:52:37 +0100 Subject: [PATCH 5/6] link to PropertyManager class in property tutorial --- Doc/tutorial_03.docu | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/tutorial_03.docu b/Doc/tutorial_03.docu index 9333b210..9125dc93 100644 --- a/Doc/tutorial_03.docu +++ b/Doc/tutorial_03.docu @@ -20,7 +20,7 @@ All three functions take two template arguments: - First, the type of the mesh element that the property is attached to (i.e. OpenMesh::VertexHandle, OpenMesh::HalfedgeHandle, OpenMesh::EdgeHandle, or OpenMesh::FaceHandle). - Second, the type of the property value that is attached to each element (e.g., \p int, \p double, etc.). -All three functions return a handle object (of type PropertyManager) that manages the lifetime of the property and provides read / write access to its values. +All three functions return a handle object (of type OpenMesh::PropertyManager) that manages the lifetime of the property and provides read / write access to its values. In this example, we will store the \c cog value (see previous example) in a vertex property instead of keeping it in a separate array. To do so, we first add a (temporary) property of the desired element type (OpenMesh::VertexHandle) and value type (\c %MyMesh::Point) to the mesh: From ab12d50e5ecc47867e26ddeaeec2dda2bdc2352e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20M=C3=B6bius?= Date: Mon, 3 Dec 2018 07:52:59 +0100 Subject: [PATCH 6/6] Updated tutorial unittest. Fixed tutorial variable names. --- Doc/Tutorial/03-properties/smooth.cc | 47 ++++++++++++++-------------- src/Unittests/unittests_tutorials.cc | 46 +++++++++++++-------------- 2 files changed, 44 insertions(+), 49 deletions(-) diff --git a/Doc/Tutorial/03-properties/smooth.cc b/Doc/Tutorial/03-properties/smooth.cc index 11866db7..37833017 100644 --- a/Doc/Tutorial/03-properties/smooth.cc +++ b/Doc/Tutorial/03-properties/smooth.cc @@ -24,31 +24,30 @@ int main(int argc, char** argv) std::cerr << "Error: Cannot read mesh from " << infile << std::endl; return 1; } - + { - // Add a vertex property storing the computed centers of gravity - auto cog = OpenMesh::makeTemporaryProperty(mesh); - - // Smooth the mesh several times - for (int i = 0; i < iterations; ++i) { - // Iterate over all vertices to compute centers of gravity - for (const auto& vh : mesh.vertices()) { - cog[vv] = {0,0,0}; - int valence = 0; - // Iterate over all 1-ring vertices around vh - for (const auto& vvh : mesh.vv_range(vh)) { - cog[vv] += mesh.point(vvh); - ++valence; - } - cog[vv] /= valence; - } - // Move all vertices to the previously computed positions - for (const auto& vh : mesh.vertices()) { - mesh.point(vv) = cog[vv]; - } - } - // The cog vertex property is removed from the mesh at the end of this scope - } + // Add a vertex property storing the computed centers of gravity + auto cog = OpenMesh::makeTemporaryProperty(mesh); + + // Smooth the mesh several times + for (int i = 0; i < iterations; ++i) { + // Iterate over all vertices to compute centers of gravity + for (const auto& vh : mesh.vertices()) { + cog[vh] = {0,0,0}; + int valence = 0; + // Iterate over all 1-ring vertices around vh + for (const auto& vvh : mesh.vv_range(vh)) { + cog[vh] += mesh.point(vvh); + ++valence; + } + cog[vh] /= valence; + } + // Move all vertices to the previously computed positions + for (const auto& vh : mesh.vertices()) { + mesh.point(vh) = cog[vh]; + } + } + } // The cog vertex property is removed from the mesh at the end of this scope // Write mesh file if (!OpenMesh::IO::read_mesh(mesh, outfile)) { diff --git a/src/Unittests/unittests_tutorials.cc b/src/Unittests/unittests_tutorials.cc index 768cf485..5503d2ab 100644 --- a/src/Unittests/unittests_tutorials.cc +++ b/src/Unittests/unittests_tutorials.cc @@ -1,5 +1,6 @@ #include +#include #include #include #include @@ -448,36 +449,31 @@ TEST_F(OpenMeshTutorials, using_custom_properties) { bool ok = OpenMesh::IO::read_mesh(mesh, "output.off"); EXPECT_TRUE(ok) << "Cannot read mesh from file 'output.off'"; - // this vertex property stores the computed centers of gravity - OpenMesh::VPropHandleT cogs; - mesh.add_property(cogs); + const int iterations = 100; - // smoothing mesh N times - MyMesh::VertexIter v_it, v_end(mesh.vertices_end()); - MyMesh::VertexVertexIter vv_it; - MyMesh::Point cog; - MyMesh::Scalar valence; - unsigned int i, N(100); - - for (i=0; i < N; ++i) { - for (v_it = mesh.vertices_begin(); v_it != v_end; ++v_it) - { - mesh.property(cogs,*v_it).vectorize(0.0f); - valence = 0.0; + // Add a vertex property storing the computed centers of gravity + auto cog = OpenMesh::makeTemporaryProperty(mesh); - for (vv_it = mesh.vv_iter( *v_it ); vv_it.is_valid(); ++vv_it) - { - mesh.property(cogs,*v_it) += mesh.point( *vv_it ); - ++valence; + // Smooth the mesh several times + for (int i = 0; i < iterations; ++i) { + // Iterate over all vertices to compute centers of gravity + for (const auto& vh : mesh.vertices()) { + cog[vh] = {0,0,0}; + int valence = 0; + // Iterate over all 1-ring vertices around vh + for (const auto& vvh : mesh.vv_range(vh)) { + cog[vh] += mesh.point(vvh); + ++valence; + } + cog[vh] /= valence; + } + // Move all vertices to the previously computed positions + for (const auto& vh : mesh.vertices()) { + mesh.point(vh) = cog[vh]; } - mesh.property(cogs,*v_it) /= valence; } - - for (v_it = mesh.vertices_begin(); v_it != v_end; ++v_it) - if ( !mesh.is_boundary( *v_it ) ) - mesh.set_point( *v_it, mesh.property(cogs,*v_it) ); - } + } // The cog vertex property is removed from the mesh at the end of this scope // write mesh ok = OpenMesh::IO::write_mesh(mesh, "smoothed_custom_properties_output.off");