Commit 9b14efaa authored by Max Lyon's avatar Max Lyon
Browse files

fix property manager for mesh properties

parent 3c52a276
...@@ -545,11 +545,39 @@ struct MPropHandleT : public BasePropHandleT<T> ...@@ -545,11 +545,39 @@ struct MPropHandleT : public BasePropHandleT<T>
{ {
typedef T Value; typedef T Value;
typedef T value_type; typedef T value_type;
typedef void Handle;
explicit MPropHandleT(int _idx=-1) : BasePropHandleT<T>(_idx) {} explicit MPropHandleT(int _idx=-1) : BasePropHandleT<T>(_idx) {}
explicit MPropHandleT(const BasePropHandleT<T>& _b) : BasePropHandleT<T>(_b) {} explicit MPropHandleT(const BasePropHandleT<T>& _b) : BasePropHandleT<T>(_b) {}
}; };
template <typename HandleT>
struct PropHandle;
template <>
struct PropHandle<VertexHandle> {
template <typename T>
using type = VPropHandleT<T>;
};
template <>
struct PropHandle<HalfedgeHandle> {
template <typename T>
using type = HPropHandleT<T>;
};
template <>
struct PropHandle<EdgeHandle> {
template <typename T>
using type = EPropHandleT<T>;
};
template <>
struct PropHandle<FaceHandle> {
template <typename T>
using type = FPropHandleT<T>;
};
} // namespace OpenMesh } // namespace OpenMesh
//============================================================================= //=============================================================================
#endif // OPENMESH_PROPERTY_HH defined #endif // OPENMESH_PROPERTY_HH defined
......
...@@ -83,6 +83,43 @@ class PropertyManager { ...@@ -83,6 +83,43 @@ class PropertyManager {
using Value = typename PROPTYPE::Value; using Value = typename PROPTYPE::Value;
using value_type = typename PROPTYPE::value_type; using value_type = typename PROPTYPE::value_type;
using Handle = typename PROPTYPE::Handle; using Handle = typename PROPTYPE::Handle;
using Self = PropertyManager<PROPTYPE, MeshT>;
private:
// Mesh properties (MPropHandleT<...>) are stored differently than the other properties.
// This class implements different behavior when copying or swapping data from one
// property manager to a another one.
template <typename PropertyManager2, typename PropHandleT>
struct StorageT;
// specialization for Mesh Properties
template <typename PropertyManager2>
struct StorageT<PropertyManager2, MPropHandleT<Value>> {
static void copy(const PropertyManager<PROPTYPE, MeshT>& from, PropertyManager2& to) {
*to = *from;
}
static void swap(PropertyManager<PROPTYPE, MeshT>& from, PropertyManager2& to) {
std::swap(*to, *from);
}
};
// definition for other Mesh Properties
template <typename PropertyManager2, typename PropHandleT>
struct StorageT {
static void copy(const PropertyManager<PROPTYPE, MeshT>& from, PropertyManager2& to) {
from.copy_to(from.mesh_.template all_elements<Handle>(), to, to.mesh_.template all_elements<Handle>());
}
static void swap(PropertyManager<PROPTYPE, MeshT>& lhs, PropertyManager2& rhs) {
std::swap(lhs.mesh_.property(lhs.prop_).data_vector(), rhs.mesh_.property(rhs.prop_).data_vector());
// resize the property to the correct size
lhs.mesh_.property(lhs.prop_).resize(lhs.mesh_.template n_elements<Handle>());
rhs.mesh_.property(rhs.prop_).resize(rhs.mesh_.template n_elements<Handle>());
}
};
using Storage = StorageT<Self, PROPTYPE>;
public:
/** /**
* @deprecated Use a constructor without \p existing and check existance with hasProperty() instead. * @deprecated Use a constructor without \p existing and check existance with hasProperty() instead.
...@@ -140,7 +177,7 @@ class PropertyManager { ...@@ -140,7 +177,7 @@ class PropertyManager {
* @param initial_value * @param initial_value
* @param propname The name of the property. * @param propname The name of the property.
*/ */
PropertyManager(const Value& intial_value, PolyConnectivity& mesh, const char *propname) : mesh_(mesh), retain_(true), name_(propname) { PropertyManager(PolyConnectivity& mesh, const char *propname, const Value& intial_value) : mesh_(mesh), retain_(true), name_(propname) {
if (!mesh_.get_property_handle(prop_, propname)) { if (!mesh_.get_property_handle(prop_, propname)) {
mesh_.add_property(prop_, propname); mesh_.add_property(prop_, propname);
set_range(mesh_.all_elements<Handle>(), intial_value); set_range(mesh_.all_elements<Handle>(), intial_value);
...@@ -166,7 +203,7 @@ class PropertyManager { ...@@ -166,7 +203,7 @@ class PropertyManager {
* *
* @param mesh The mesh on which to create the property. * @param mesh The mesh on which to create the property.
*/ */
PropertyManager(const Value& intial_value, PolyConnectivity& mesh) : mesh_(mesh), retain_(false), name_("") { PropertyManager(PolyConnectivity& mesh, const Value& intial_value) : mesh_(mesh), retain_(false), name_("") {
mesh_.add_property(prop_, name_); mesh_.add_property(prop_, name_);
set_range(mesh_.all_elements<Handle>(), intial_value); set_range(mesh_.all_elements<Handle>(), intial_value);
} }
...@@ -197,7 +234,7 @@ class PropertyManager { ...@@ -197,7 +234,7 @@ class PropertyManager {
else // unnamed property -> create a property manager refering to a new property and copy the contents else // unnamed property -> create a property manager refering to a new property and copy the contents
{ {
mesh_.add_property(prop_, name_); mesh_.add_property(prop_, name_);
rhs.copy_to(rhs.mesh_.template all_elements<Handle>(), *this, mesh_.all_elements<Handle>()); Storage::copy(rhs, *this);
} }
} }
...@@ -206,7 +243,7 @@ class PropertyManager { ...@@ -206,7 +243,7 @@ class PropertyManager {
if (&mesh_ == &rhs.mesh_ && prop_ == rhs.prop_) if (&mesh_ == &rhs.mesh_ && prop_ == rhs.prop_)
; // nothing to do ; // nothing to do
else else
rhs.copy_to(rhs.mesh_.template all_elements<Handle>(), *this, mesh_.all_elements<Handle>()); Storage::copy(rhs, *this);
return *this; return *this;
} }
...@@ -280,14 +317,12 @@ class PropertyManager { ...@@ -280,14 +317,12 @@ class PropertyManager {
if (rhs.retain_) if (rhs.retain_)
{ {
// retained properties cannot be invalidated. Copy instead // retained properties cannot be invalidated. Copy instead
rhs.copy_to(rhs.mesh_.template all_elements<Handle>(), *this, mesh_.all_elements<Handle>()); Storage::copy(rhs, *this);
} }
else else
{ {
// switch the data stored in the properties // switch the data stored in the properties
std::swap(mesh_.property(prop_).data_vector(), rhs.mesh_.property(rhs.prop_).data_vector()); Storage::swap(rhs, *this);
// resize the property to the correct size
mesh_.property(prop_).resize(mesh_.n_elements<Handle>());
// remove the property from rhs // remove the property from rhs
rhs.mesh_.remove_property(rhs.prop_); rhs.mesh_.remove_property(rhs.prop_);
// invalidate prop_ // invalidate prop_
...@@ -560,6 +595,7 @@ class PropertyManager { ...@@ -560,6 +595,7 @@ class PropertyManager {
dst_range.begin(), dst_range.end()); dst_range.begin(), dst_range.end());
} }
/** /**
* Copy the values of a property from a source range to * Copy the values of a property from a source range to
* a target range. The source range must not be smaller than the * a target range. The source range must not be smaller than the
...@@ -602,6 +638,8 @@ class PropertyManager { ...@@ -602,6 +638,8 @@ class PropertyManager {
}; };
/** @relates PropertyManager /** @relates PropertyManager
*
* @deprecated Temporary properties should not have a name.
* *
* Creates a new property whose lifetime is limited to the current scope. * Creates a new property whose lifetime is limited to the current scope.
* *
...@@ -628,30 +666,69 @@ class PropertyManager { ...@@ -628,30 +666,69 @@ class PropertyManager {
*/ */
template<typename ElementT, typename T> template<typename ElementT, typename T>
PropertyManager<typename HandleToPropHandle<ElementT, T>::type> PropertyManager<typename HandleToPropHandle<ElementT, T>::type>
makeTemporaryProperty(PolyConnectivity &mesh, const char *propname = "") { OM_DEPRECATED("Named temporary properties are deprecated. Either create a temporary without name or a non-temporary with name")
makeTemporaryProperty(PolyConnectivity &mesh, const char *propname) {
return PropertyManager<typename HandleToPropHandle<ElementT, T>::type>(mesh, propname, false); return PropertyManager<typename HandleToPropHandle<ElementT, T>::type>(mesh, propname, false);
} }
///// shortcut or makeTemporaryrProperty<OpenMesh::VertexHandle, T, MeshT> /** @relates PropertyManager
//template<typename T, typename MeshT> *
//PropertyManager<typename HandleToPropHandle<OpenMesh::VertexHandle, T>::type, MeshT> * Creates a new property whose lifetime is limited to the current scope.
//makeTemporaryVertexProperty(MeshT &mesh, const char *propname) { return makeTemporaryProperty<OpenMesh::VertexHandle, T>(mesh, propname); } *
* Used for temporary properties. Shadows any existing properties of
///// shortcut or makeTemporaryrProperty<OpenMesh::HalfedgeHandle, T, MeshT> * matching name and type.
//template<typename T, typename MeshT> *
//PropertyManager<typename HandleToPropHandle<OpenMesh::HalfedgeHandle, T>::type, MeshT> * Example:
//makeTemporaryHalfedgeProperty(MeshT &mesh, const char *propname) { return makeTemporaryProperty<OpenMesh::HalfedgeHandle, T>(mesh, propname); } * @code
* PolyMesh m;
///// shortcut or makeTemporaryrProperty<OpenMesh::EdgeHandle, T, MeshT> * {
//template<typename T, typename MeshT> * auto is_quad = makeTemporaryProperty<FaceHandle, bool>(m);
//PropertyManager<typename HandleToPropHandle<OpenMesh::EdgeHandle, T>::type, MeshT> * for (auto& fh : m.faces()) {
//makeTemporaryEdgeProperty(MeshT &mesh, const char *propname) { return makeTemporaryProperty<OpenMesh::EdgeHandle, T>(mesh, propname); } * 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
* @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.
* @returns A PropertyManager handling the lifecycle of the property
*/
template<typename ElementT, typename T>
PropertyManager<typename HandleToPropHandle<ElementT, T>::type>
makeTemporaryProperty(PolyConnectivity &mesh) {
return PropertyManager<typename HandleToPropHandle<ElementT, T>::type>(mesh);
}
///// shortcut or makeTemporaryrProperty<OpenMesh::FaceHandle, T, MeshT>
//template<typename T, typename MeshT>
//PropertyManager<typename HandleToPropHandle<OpenMesh::FaceHandle, T>::type, MeshT>
//makeTemporaryFaceProperty(MeshT &mesh, const char *propname) { return makeTemporaryProperty<OpenMesh::FaceHandle, T>(mesh, propname); }
/** @relates PropertyManager
*
* Tests whether a property with the given element type, value type, and name is
* present on the given mesh.
*
* * Example:
* @code
* PolyMesh m;
* if (hasProperty<FaceHandle, bool>(m, "is_quad")) {
* // We now know the property exists: getProperty won't throw.
* auto is_quad = getProperty<FaceHandle, bool>(m, "is_quad");
* // Use is_quad here.
* }
* @endcode
*
* @param mesh The mesh in question
* @param propname The property name of the expected property
* @tparam ElementT Element type of the expected property, e.g. VertexHandle, HalfedgeHandle, etc.
* @tparam T Value type of the expected property, e.g., \p double, \p int, etc.
* @tparam MeshT Type of the mesh. Can often be inferred from \p mesh
*/
template<typename ElementT, typename T>
bool
hasProperty(const PolyConnectivity &mesh, const char *propname) {
typename HandleToPropHandle<ElementT, T>::type ph;
return mesh.get_property_handle(ph, propname);
}
/** @relates PropertyManager /** @relates PropertyManager
* *
...@@ -683,7 +760,13 @@ makeTemporaryProperty(PolyConnectivity &mesh, const char *propname = "") { ...@@ -683,7 +760,13 @@ makeTemporaryProperty(PolyConnectivity &mesh, const char *propname = "") {
template<typename ElementT, typename T> template<typename ElementT, typename T>
PropertyManager<typename HandleToPropHandle<ElementT, T>::type> PropertyManager<typename HandleToPropHandle<ElementT, T>::type>
getProperty(PolyConnectivity &mesh, const char *propname) { getProperty(PolyConnectivity &mesh, const char *propname) {
return PropertyManager<typename HandleToPropHandle<ElementT, T>::type>(mesh, propname, true); if (!hasProperty<ElementT, T>(mesh, propname))
{
std::ostringstream oss;
oss << "Requested property handle \"" << propname << "\" does not exist.";
throw std::runtime_error(oss.str());
}
return PropertyManager<typename HandleToPropHandle<ElementT, T>::type>(mesh, propname);
} }
/** @relates PropertyManager /** @relates PropertyManager
...@@ -721,34 +804,6 @@ getOrMakeProperty(PolyConnectivity &mesh, const char *propname) { ...@@ -721,34 +804,6 @@ getOrMakeProperty(PolyConnectivity &mesh, const char *propname) {
return PropertyManager<typename HandleToPropHandle<ElementT, T>::type>::createIfNotExists(mesh, propname); return PropertyManager<typename HandleToPropHandle<ElementT, T>::type>::createIfNotExists(mesh, propname);
} }
/** @relates PropertyManager
*
* Tests whether a property with the given element type, value type, and name is
* present on the given mesh.
*
* * Example:
* @code
* PolyMesh m;
* if (hasProperty<FaceHandle, bool>(m, "is_quad")) {
* // We now know the property exists: getProperty won't throw.
* auto is_quad = getProperty<FaceHandle, bool>(m, "is_quad");
* // Use is_quad here.
* }
* @endcode
*
* @param mesh The mesh in question
* @param propname The property name of the expected property
* @tparam ElementT Element type of the expected property, e.g. VertexHandle, HalfedgeHandle, etc.
* @tparam T Value type of the expected property, e.g., \p double, \p int, etc.
* @tparam MeshT Type of the mesh. Can often be inferred from \p mesh
*/
template<typename ElementT, typename T>
bool
hasProperty(const PolyConnectivity &mesh, const char *propname) {
typename HandleToPropHandle<ElementT, T>::type ph;
return mesh.get_property_handle(ph, propname);
}
/** @relates PropertyManager /** @relates PropertyManager
* @deprecated Use makeTemporaryProperty() instead. * @deprecated Use makeTemporaryProperty() instead.
* *
...@@ -853,6 +908,8 @@ getPointsProperty(MeshT &mesh) { ...@@ -853,6 +908,8 @@ getPointsProperty(MeshT &mesh) {
return PropertyManager<OpenMesh::VPropHandleT<typename MeshT::Point>>(mesh, mesh.points_property_handle()); return PropertyManager<OpenMesh::VPropHandleT<typename MeshT::Point>>(mesh, mesh.points_property_handle());
} }
template <typename HandleT, typename T>
using Prop = PropertyManager<typename PropHandle<HandleT>::template type<T>>;
template <typename T> template <typename T>
using VProp = PropertyManager<OpenMesh::VPropHandleT<T>>; using VProp = PropertyManager<OpenMesh::VPropHandleT<T>>;
...@@ -866,6 +923,9 @@ using EProp = PropertyManager<OpenMesh::EPropHandleT<T>>; ...@@ -866,6 +923,9 @@ using EProp = PropertyManager<OpenMesh::EPropHandleT<T>>;
template <typename T> template <typename T>
using FProp = PropertyManager<OpenMesh::FPropHandleT<T>>; using FProp = PropertyManager<OpenMesh::FPropHandleT<T>>;
template <typename T>
using MProp = PropertyManager<OpenMesh::MPropHandleT<T>>;
} /* namespace OpenMesh */ } /* namespace OpenMesh */
#endif /* PROPERTYMANAGER_HH_ */ #endif /* PROPERTYMANAGER_HH_ */
...@@ -130,55 +130,6 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) { ...@@ -130,55 +130,6 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) {
} }
} }
/*
* ====================================================================
* Factory Functions
* ====================================================================
*/
/*
* Temporary property
*/
TEST_F(OpenMeshPropertyManager, cpp11_temp_property) {
using handle_type = OpenMesh::VPropHandleT<int>;
const auto prop_name = "pm_v_test_property";
ASSERT_FALSE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
{
auto vprop = OpenMesh::makeTemporaryProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name);
static_cast<void>(vprop); // Unused variable
ASSERT_TRUE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
}
ASSERT_FALSE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
}
/*
* Two temporary properties on a mesh using the same name and type. The second
* (inner) one shadows the first (outer) one instead of aliasing.
*/
TEST_F(OpenMeshPropertyManager, cpp11_temp_property_shadowing) {
auto vh = mesh_.add_vertex({0,0,0}); // Dummy vertex to attach properties to
const auto prop_name = "pm_v_test_property";
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::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_
}
// Ensure outer_prop still exists and its data has not been overwritten by inner_prop
ASSERT_TRUE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
ASSERT_EQ(100, outer_prop[vh]);
}
/* /*
* In sequence: * In sequence:
* - add a persistent property to a mesh * - add a persistent property to a mesh
...@@ -315,8 +266,8 @@ TEST_F(OpenMeshPropertyManager, property_copying_same_mesh) { ...@@ -315,8 +266,8 @@ TEST_F(OpenMeshPropertyManager, property_copying_same_mesh) {
// unnamed to unnamed // unnamed to unnamed
{ {
auto prop1 = OpenMesh::VProp<int>(3, mesh_); auto prop1 = OpenMesh::VProp<int>(mesh_, 3);
auto prop2 = OpenMesh::VProp<int>(0, mesh_); auto prop2 = OpenMesh::VProp<int>(mesh_, 0);
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 3) << "Property not initialized correctly"; EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 3) << "Property not initialized correctly";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly"; EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
...@@ -341,7 +292,7 @@ TEST_F(OpenMeshPropertyManager, property_copying_same_mesh) { ...@@ -341,7 +292,7 @@ TEST_F(OpenMeshPropertyManager, property_copying_same_mesh) {
// unnamed to named // unnamed to named
{ {
auto prop1 = OpenMesh::VProp<int>(mesh_); auto prop1 = OpenMesh::VProp<int>(mesh_);
auto prop2 = OpenMesh::VProp<int>(0, mesh_, "ids"); auto prop2 = OpenMesh::VProp<int>(mesh_, "ids", 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly"; EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
for (auto vh : mesh_.vertices()) for (auto vh : mesh_.vertices())
...@@ -579,8 +530,8 @@ TEST_F(OpenMeshPropertyManager, property_copying_different_mesh) { ...@@ -579,8 +530,8 @@ TEST_F(OpenMeshPropertyManager, property_copying_different_mesh) {
// unnamed to unnamed // unnamed to unnamed
{ {
auto prop1 = OpenMesh::VProp<int>(3, mesh_); auto prop1 = OpenMesh::VProp<int>(mesh_, 3);
auto prop2 = OpenMesh::VProp<int>(0, copy); auto prop2 = OpenMesh::VProp<int>(copy, 0);
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 3) << "Property not initialized correctly"; EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 3) << "Property not initialized correctly";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly"; EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
...@@ -600,13 +551,13 @@ TEST_F(OpenMeshPropertyManager, property_copying_different_mesh) { ...@@ -600,13 +551,13 @@ TEST_F(OpenMeshPropertyManager, property_copying_different_mesh) {
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 0) << "Property not copied correctly"; EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 0) << "Property not copied correctly";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly"; EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
EXPECT_NO_FATAL_FAILURE(prop2[OpenMesh::VertexHandle(copy.n_vertices()-1)]) << "Property not correctly resized"; EXPECT_NO_FATAL_FAILURE(prop2[OpenMesh::VertexHandle(static_cast<int>(copy.n_vertices())-1)]) << "Property not correctly resized";
} }
// unnamed to named // unnamed to named
{ {
auto prop1 = OpenMesh::VProp<int>(mesh_); auto prop1 = OpenMesh::VProp<int>(mesh_);
auto prop2 = OpenMesh::VProp<int>(0, copy, "ids"); auto prop2 = OpenMesh::VProp<int>(copy, "ids", 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly"; EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
for (auto vh : mesh_.vertices()) for (auto vh : mesh_.vertices())
...@@ -731,7 +682,7 @@ TEST_F(OpenMeshPropertyManager, property_moving_different_mesh) { ...@@ -731,7 +682,7 @@ TEST_F(OpenMeshPropertyManager, property_moving_different_mesh) {
EXPECT_FALSE(prop1.isValid()) << "prop1 not invalidated after moving"; EXPECT_FALSE(prop1.isValid()) << "prop1 not invalidated after moving";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly"; EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
EXPECT_NO_FATAL_FAILURE(prop2[OpenMesh::VertexHandle(copy.n_vertices()-1)]) << "Property not correctly resized"; EXPECT_NO_FATAL_FAILURE(prop2[OpenMesh::VertexHandle(static_cast<int>(copy.n_vertices())-1)]) << "Property not correctly resized";
} }
// unnamed to named // unnamed to named
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment