Merge branch 'rename_property_manager_factories' into 'master'

rename PropertyManager factory functions

See merge request OpenMesh/OpenMesh!193
This commit is contained in:
Jan Möbius
2018-12-03 09:49:52 +01:00
6 changed files with 307 additions and 161 deletions

View File

@@ -0,0 +1,40 @@
#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_

View File

@@ -49,6 +49,8 @@
#ifndef PROPERTYMANAGER_HH_
#define PROPERTYMANAGER_HH_
#include <OpenMesh/Core/System/config.h>
#include <OpenMesh/Core/Utils/HandleToPropHandle.hh>
#include <sstream>
#include <stdexcept>
#include <string>
@@ -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<VPropHandleT<bool>>(mesh, "visited.plugin-example.i8.informatik.rwth-aachen.de");
* {
* TriMesh mesh;
* auto visited = makeTemporaryProperty<VertexHandle, bool>(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<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>
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<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
* PropertyManager.
*
* Intended for temporary properties. Shadows any existsing properties of
* Intended for temporary properties. Shadows any existing properties of
* matching name and type.
*/
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);
}
/** \relates PropertyManager
* @deprecated Use getProperty() instead.
*
* Creates a non-owning wrapper for an existing mesh property (no lifecycle
* management).
*
@@ -516,22 +614,28 @@ PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromNew(MeshT &mesh, const c
* matching type exists.
*/
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);
}
/** \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<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);
}
/** \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<PROPTYPE, MeshT> 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,

View File

@@ -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<typename PropHandle, typename Mesh>
bool has_property(const Mesh& _mesh, const std::string& _name) {
@@ -154,7 +151,7 @@ TEST_F(OpenMeshPropertyManager, cpp11_temp_property) {
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
ASSERT_TRUE(has_property<handle_type>(mesh_, prop_name));
}
@@ -172,13 +169,13 @@ TEST_F(OpenMeshPropertyManager, cpp11_temp_property_shadowing) {
using handle_type = OpenMesh::VPropHandleT<int>;
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;
ASSERT_EQ(100, outer_prop[vh]);
{
// 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;
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<handle_type>(mesh_, prop_name));
{
auto prop = OpenMesh::makePropertyManagerFromExistingOrNew<handle_type>(mesh_, prop_name);
auto prop = OpenMesh::getOrMakeProperty<OpenMesh::VertexHandle, int>(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<handle_type>(mesh_, prop_name);
auto prop = OpenMesh::getOrMakeProperty<OpenMesh::VertexHandle, int>(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<handle_type>(mesh_, prop_name);
auto prop = OpenMesh::getProperty<OpenMesh::VertexHandle, int>(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<handle_type>(mesh_, "wrong_property_name"), std::runtime_error);
auto code_that_throws = [&](){
OpenMesh::getProperty<OpenMesh::VertexHandle, int>(mesh_, "wrong_prop_name");
};
ASSERT_THROW(code_that_throws(), std::runtime_error);
}
ASSERT_TRUE(has_property<handle_type>(mesh_, prop_name));
}
#endif
}

View File

@@ -1,5 +1,6 @@
#include <gtest/gtest.h>
#include <OpenMesh/Core/Utils/PropertyManager.hh>
#include <Unittests/unittests_common.hh>
#include <string>
#include <map>
@@ -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<MyMesh::Point> 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<OpenMesh::VertexHandle, MyMesh::Point>(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");