Commit 1a0a76dd authored by Jan Möbius's avatar Jan Möbius

Merge branch 'rename_property_manager_factories' into 'master'

rename PropertyManager factory functions

See merge request !193
parents 19bd618a ab12d50e
Pipeline #7967 passed with stages
in 77 minutes and 56 seconds
#include <iostream>
#include <vector>
// --------------------
#include <OpenMesh/Core/IO/MeshIO.hh> #include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh> #include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
#include <OpenMesh/Core/Utils/PropertyManager.hh>
typedef OpenMesh::TriMesh_ArrayKernelT<> MyMesh; #include <iostream>
#include <vector>
using MyMesh = OpenMesh::TriMesh_ArrayKernelT<>;
int main(int argc, char **argv) int main(int argc, char** argv)
{ {
MyMesh mesh; // Read command line options
MyMesh mesh;
if (argc != 4) {
// check command line options std::cerr << "Usage: " << argv[0] << " #iterations infile outfile" << std::endl;
if (argc != 4) return 1;
{
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<MyMesh::Point> 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;
} }
const int iterations = argv[1];
const std::string infile = argv[2];
const std::string outfile = argv[3];
for (v_it=mesh.vertices_begin(); v_it!=v_end; ++v_it) // Read mesh file
if ( !mesh.is_boundary( *v_it ) ) if (!OpenMesh::IO::read_mesh(mesh, infile)) {
mesh.set_point( *v_it, mesh.property(cogs,*v_it) ); std::cerr << "Error: Cannot read mesh from " << infile << std::endl;
} return 1;
}
// 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<OpenMesh::VertexHandle, MyMesh::Point>(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)) {
std::cerr << "Error: Cannot write mesh to " << outfile << std::endl;
return 1;
}
} }
/** \page tutorial_03 Using (custom) properties /** \page tutorial_03 Using (custom) properties
This examples shows: 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 - How to get and set the value of a custom property
In the last example we computed the barycenter of each vertex' In the last example we computed the barycenter of each vertex'
...@@ -11,44 +11,69 @@ let %OpenMesh manage the data. ...@@ -11,44 +11,69 @@ let %OpenMesh manage the data.
It would be even more helpful if we could attach such properties It would be even more helpful if we could attach such properties
dynamically to the mesh. dynamically to the mesh.
%OpenMesh provides dynamic properties, which can be attached to each Custom properties can be conveniently created and attached to meshes with the following functions:
mesh entity (vertex, face, edge, halfedge, and the mesh itself). We - makeTemporaryProperty() creates a property that is temporary to the current scope.
distinguish between custom and standard properties. A custom property - getOrMakeProperty() is used for creating and accessing permanent named properties on a mesh.
is any user-defined property and is accessed via the member function - getProperty() is used for accessing an existing permanent named property on a mesh.
\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.
In this example we will store the \c cog-value (see previous example) All three functions take two template arguments:
in an additional vertex property instead of keeping it in a separate - First, the type of the mesh element that the property is attached to (i.e. OpenMesh::VertexHandle, OpenMesh::HalfedgeHandle, OpenMesh::EdgeHandle, or OpenMesh::FaceHandle).
array. To do so we define first a so-called property handle with the desired - Second, the type of the property value that is attached to each element (e.g., \p int, \p double, etc.).
type (\c MyMesh::Point) and register the handle at the mesh:
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:
\dontinclude 03-properties/smooth.cc \dontinclude 03-properties/smooth.cc
\skipline vertex property stores \skipline makeTemporaryProperty
\until mesh.add
<br>The \c mesh allocates enough memory to hold as many elements of type Enough memory is allocated to hold as many values of \c %MyMesh::Point as there are vertices.
\c MyMesh::Point as number of vertices exist, and of course the mesh All insert and delete operations on the mesh are synchronized with the attached properties.
synchronizes all insert and delete operations on the vertices with the
vertex properties.
Once the wanted property is registered we can use the property to Once the property is created, we can use it to compute the centers of the neighborhood of each vertex:
calculate the barycenter of the neighborhood of each vertex \c v_it
\dontinclude 03-properties/smooth.cc \skipline mesh.vertices
\skipline vv_it= \until cog[vv] /= valence
\until } \until }
\until mesh.prop
<br>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.vertices
\skipline mesh.set_point \until mesh.point
\until }
---
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<OpenMesh::FaceHandle, double>(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<OpenMesh::FaceHandle, double>(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).
---
<br>Below is the complete source code: Below is the complete source code:
\include 03-properties/smooth.cc \include 03-properties/smooth.cc
*/ */
\ No newline at end of file
#ifndef HANDLETOPROPHANDLE_HH_
#define HANDLETOPROPHANDLE_HH_
#include <OpenMesh/Core/Mesh/Handles.hh>
#include <OpenMesh/Core/Utils/Property.hh>
namespace OpenMesh {
template<typename ElementT, typename T>
struct HandleToPropHandle {
};
template<typename T>
struct HandleToPropHandle<OpenMesh::VertexHandle, T> {
using type = OpenMesh::VPropHandleT<T>;
};
template<typename T>
struct HandleToPropHandle<OpenMesh::HalfedgeHandle, T> {
using type = OpenMesh::HPropHandleT<T>;
};
template<typename T>
struct HandleToPropHandle<OpenMesh::EdgeHandle, T> {
using type = OpenMesh::EPropHandleT<T>;
};
template<typename T>
struct HandleToPropHandle<OpenMesh::FaceHandle, T> {
using type = OpenMesh::FPropHandleT<T>;
};
template<typename T>
struct HandleToPropHandle<void, T> {
using type = OpenMesh::MPropHandleT<T>;
};
} // namespace OpenMesh
#endif // HANDLETOPROPHANDLE_HH_
\ No newline at end of file
...@@ -49,6 +49,8 @@ ...@@ -49,6 +49,8 @@
#ifndef PROPERTYMANAGER_HH_ #ifndef PROPERTYMANAGER_HH_
#define PROPERTYMANAGER_HH_ #define PROPERTYMANAGER_HH_
#include <OpenMesh/Core/System/config.h>
#include <OpenMesh/Core/Utils/HandleToPropHandle.hh>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
...@@ -60,35 +62,23 @@ namespace OpenMesh { ...@@ -60,35 +62,23 @@ namespace OpenMesh {
* It also defines convenience operators to access the encapsulated * It also defines convenience operators to access the encapsulated
* property's value. * property's value.
* *
* For C++11, it is recommended to use the factory functions * It is recommended to use the factory functions
* makePropertyManagerFromNew, makePropertyManagerFromExisting, * makeTemporaryProperty(), getProperty(), and getOrMakeProperty()
* makePropertyManagerFromExistingOrNew to construct a PropertyManager, e.g. * to construct a PropertyManager, e.g.
* *
* \code * \code
* TriMesh mesh; * {
* auto visited = makePropertyManagerFromNew<VPropHandleT<bool>>(mesh, "visited.plugin-example.i8.informatik.rwth-aachen.de"); * TriMesh mesh;
* auto visited = makeTemporaryProperty<VertexHandle, bool>(mesh);
* *
* for (auto vh : mesh.vertices()) { * for (auto vh : mesh.vertices()) {
* if (!visited[vh]) { * if (!visited[vh]) {
* visitComponent(mesh, vh, visited); * visitComponent(mesh, vh, visited);
* }
* } * }
* // The property is automatically removed at the end of the scope
* } * }
* \endcode * \endcode
*
* For C++98, it is usually more convenient to use the constructor explicitly,
* i.e.
*
* \code
* TriMesh mesh;
* PropertyManager<VPropHandleT<bool>, 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<typename PROPTYPE, typename MeshT> template<typename PROPTYPE, typename MeshT>
class PropertyManager { class PropertyManager {
...@@ -493,19 +483,127 @@ class PropertyManager { ...@@ -493,19 +483,127 @@ class PropertyManager {
std::string name_; 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<FaceHandle, bool>(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<typename ElementT, typename T, typename MeshT>
PropertyManager<typename HandleToPropHandle<ElementT, T>::type, MeshT>
makeTemporaryProperty(MeshT &mesh, const char *propname = "") {
return PropertyManager<typename HandleToPropHandle<ElementT, T>::type, MeshT>(mesh, propname, false);
}
/** @relates PropertyManager
*
* Obtains a handle to a named property.
*
* Example:
* @code
* PolyMesh m;
* {
* try {
* auto is_quad = getProperty<FaceHandle, bool>(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<typename ElementT, typename T, typename MeshT>
PropertyManager<typename HandleToPropHandle<ElementT, T>::type, MeshT>
getProperty(MeshT &mesh, const char *propname) {
return PropertyManager<typename HandleToPropHandle<ElementT, T>::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<FaceHandle, bool>(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<FaceHandle, bool>(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<typename ElementT, typename T, typename MeshT>
PropertyManager<typename HandleToPropHandle<ElementT, T>::type, MeshT>
getOrMakeProperty(MeshT &mesh, const char *propname) {
return PropertyManager<typename HandleToPropHandle<ElementT, T>::type, MeshT>::createIfNotExists(mesh, propname);
}
/** @relates PropertyManager
* @deprecated Use makeTemporaryProperty() instead.
*
* Creates a new property whose lifecycle is managed by the returned * Creates a new property whose lifecycle is managed by the returned
* PropertyManager. * PropertyManager.
* *
* Intended for temporary properties. Shadows any existsing properties of * Intended for temporary properties. Shadows any existing properties of
* matching name and type. * matching name and type.
*/ */
template<typename PROPTYPE, typename MeshT> template<typename PROPTYPE, typename MeshT>
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromNew(MeshT &mesh, const char *propname) { OM_DEPRECATED("Use makeTemporaryProperty instead.")
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromNew(MeshT &mesh, const char *propname)
{
return PropertyManager<PROPTYPE, MeshT>(mesh, propname, false); return PropertyManager<PROPTYPE, MeshT>(mesh, propname, false);
} }
/** \relates PropertyManager /** \relates PropertyManager
* @deprecated Use getProperty() instead.
*
* Creates a non-owning wrapper for an existing mesh property (no lifecycle * Creates a non-owning wrapper for an existing mesh property (no lifecycle
* management). * management).
* *
...@@ -516,22 +614,28 @@ PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromNew(MeshT &mesh, const c ...@@ -516,22 +614,28 @@ PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromNew(MeshT &mesh, const c
* matching type exists. * matching type exists.
*/ */
template<typename PROPTYPE, typename MeshT> template<typename PROPTYPE, typename MeshT>
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExisting(MeshT &mesh, const char *propname) { OM_DEPRECATED("Use getProperty instead.")
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExisting(MeshT &mesh, const char *propname)
{
return PropertyManager<PROPTYPE, MeshT>(mesh, propname, true); return PropertyManager<PROPTYPE, MeshT>(mesh, propname, true);
} }
/** \relates PropertyManager /** @relates PropertyManager
* @deprecated Use getOrMakeProperty() instead.
*
* Creates a non-owning wrapper for a mesh property (no lifecycle management). * Creates a non-owning wrapper for a mesh property (no lifecycle management).
* If the given property does not exist, it is created. * If the given property does not exist, it is created.
* *
* Intended for creating or accessing persistent properties. * Intended for creating or accessing persistent properties.
*/ */
template<typename PROPTYPE, typename MeshT> template<typename PROPTYPE, typename MeshT>
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExistingOrNew(MeshT &mesh, const char *propname) { OM_DEPRECATED("Use getOrMakeProperty instead.")
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExistingOrNew(MeshT &mesh, const char *propname)
{
return PropertyManager<PROPTYPE, MeshT>::createIfNotExists(mesh, propname); return PropertyManager<PROPTYPE, MeshT>::createIfNotExists(mesh, propname);
} }
/** \relates PropertyManager /** @relates PropertyManager
* Like the two parameter version of makePropertyManagerFromExistingOrNew() * Like the two parameter version of makePropertyManagerFromExistingOrNew()
* except it initializes the property with the specified value over the * except it initializes the property with the specified value over the
* specified range if it needs to be created. If the property already exists, * specified range if it needs to be created. If the property already exists,
...@@ -552,7 +656,7 @@ PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExistingOrNew( ...@@ -552,7 +656,7 @@ PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExistingOrNew(
mesh, propname, begin, end, init_value); mesh, propname, begin, end, init_value);
} }
/** \relates PropertyManager /** @relates PropertyManager
* Like the two parameter version of makePropertyManagerFromExistingOrNew() * Like the two parameter version of makePropertyManagerFromExistingOrNew()
* except it initializes the property with the specified value over the * except it initializes the property with the specified value over the
* specified range if it needs to be created. If the property already exists, * specified range if it needs to be created. If the property already exists,
......
...@@ -93,7 +93,6 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) { ...@@ -93,7 +93,6 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) {
ASSERT_TRUE(pm_f_bool[*f_it]); 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. * Same thing again, this time with C++11 ranges.
*/ */
...@@ -129,15 +128,13 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) { ...@@ -129,15 +128,13 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) {
f_it != f_end; ++f_it) f_it != f_end; ++f_it)
ASSERT_TRUE(pm_f_bool[*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<typename PropHandle, typename Mesh> template<typename PropHandle, typename Mesh>
bool has_property(const Mesh& _mesh, const std::string& _name) { bool has_property(const Mesh& _mesh, const std::string& _name) {
...@@ -154,7 +151,7 @@ TEST_F(OpenMeshPropertyManager, cpp11_temp_property) { ...@@ -154,7 +151,7 @@ TEST_F(OpenMeshPropertyManager, cpp11_temp_property) {
ASSERT_FALSE(has_property<handle_type>(mesh_, prop_name)); ASSERT_FALSE(has_property<handle_type>(mesh_, prop_name));
{ {
auto vprop = OpenMesh::makePropertyManagerFromNew<handle_type>(mesh_, prop_name); auto vprop = OpenMesh::makeTemporaryProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name);
static_cast<void>(vprop); // Unused variable static_cast<void>(vprop); // Unused variable
ASSERT_TRUE(has_property<handle_type>(mesh_, prop_name)); ASSERT_TRUE(has_property<handle_type>(mesh_, prop_name));
} }
...@@ -172,13 +169,13 @@ TEST_F(OpenMeshPropertyManager, cpp11_temp_property_shadowing) { ...@@ -172,13 +169,13 @@ TEST_F(OpenMeshPropertyManager, cpp11_temp_property_shadowing) {
using handle_type = OpenMesh::VPropHandleT<int>; using handle_type = OpenMesh::VPropHandleT<int>;
const auto prop_name = "pm_v_test_property"; const auto prop_name = "pm_v_test_property";
auto outer_prop = OpenMesh::makePropertyManagerFromNew<handle_type>(mesh_, prop_name); auto outer_prop = OpenMesh::makeTemporaryProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name);
outer_prop[vh] = 100; outer_prop[vh] = 100;
ASSERT_EQ(100, outer_prop[vh]); ASSERT_EQ(100, outer_prop[vh]);
{ {
// inner_prop uses same type and name as outer_prop // inner_prop uses same type and name as outer_prop
auto inner_prop = OpenMesh::makePropertyManagerFromNew<handle_type>(mesh_, prop_name); auto inner_prop = OpenMesh::makeTemporaryProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name);
inner_prop[vh] = 200; inner_prop[vh] = 200;
ASSERT_EQ(200, inner_prop[vh]); ASSERT_EQ(200, inner_prop[vh]);
// End of scope: inner_prop is removed from mesh_ // End of scope: inner_prop is removed from mesh_
...@@ -205,7 +202,7 @@ TEST_F(OpenMeshPropertyManager, cpp11_persistent_and_non_owning_properties) { ...@@ -205,7 +202,7 @@ TEST_F(OpenMeshPropertyManager, cpp11_persistent_and_non_owning_properties) {
ASSERT_FALSE(has_property<handle_type>(mesh_, prop_name)); ASSERT_FALSE(has_property<handle_type>(mesh_, prop_name));
{ {
auto prop = OpenMesh::makePropertyManagerFromExistingOrNew<handle_type>(mesh_, prop_name); auto prop = OpenMesh::getOrMakeProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name);