/* ========================================================================= * * * * OpenMesh * * Copyright (c) 2001-2015, RWTH-Aachen University * * Department of Computer Graphics and Multimedia * * All rights reserved. * * www.openmesh.org * * * *---------------------------------------------------------------------------* * This file is part of OpenMesh. * *---------------------------------------------------------------------------* * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * * are met: * * * * 1. Redistributions of source code must retain the above copyright notice, * * this list of conditions and the following disclaimer. * * * * 2. Redistributions in binary form must reproduce the above copyright * * notice, this list of conditions and the following disclaimer in the * * documentation and/or other materials provided with the distribution. * * * * 3. Neither the name of the copyright holder nor the names of its * * contributors may be used to endorse or promote products derived from * * this software without specific prior written permission. * * * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER * * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * * ========================================================================= */ /*===========================================================================*\ * * * $Revision$ * * $Date$ * * * \*===========================================================================*/ #ifndef PROPERTYMANAGER_HH_ #define PROPERTYMANAGER_HH_ #include #include #include namespace OpenMesh { /** * This class is intended to manage the lifecycle of properties. * 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. * * \code * TriMesh mesh; * auto visited = makePropertyManagerFromNew>(mesh, "visited.plugin-example.i8.informatik.rwth-aachen.de"); * * for (auto vh : mesh.vertices()) { * if (!visited[vh]) { * visitComponent(mesh, vh, visited); * } * } * \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 { #if (defined(_MSC_VER) && (_MSC_VER >= 1900)) || __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__) public: PropertyManager(const PropertyManager&) = delete; PropertyManager& operator=(const PropertyManager&) = delete; #else private: /** * Noncopyable because there aren't no straightforward copy semantics. */ PropertyManager(const PropertyManager&); /** * Noncopyable because there aren't no straightforward copy semantics. */ PropertyManager& operator=(const PropertyManager&); #endif public: /** * Constructor. * * Throws an \p std::runtime_error if \p existing is true and * no property named \p propname of the appropriate property type * exists. * * @param mesh The mesh on which to create the property. * @param propname The name of the property. * @param existing If false, a new property is created and its lifecycle is managed (i.e. * the property is deleted upon destruction of the PropertyManager instance). If true, * the instance merely acts as a convenience wrapper around an existing property with no * lifecycle management whatsoever. * * @see PropertyManager::createIfNotExists, makePropertyManagerFromNew, * makePropertyManagerFromExisting, makePropertyManagerFromExistingOrNew */ PropertyManager(MeshT &mesh, const char *propname, bool existing = false) : mesh_(&mesh), retain_(existing), name_(propname) { if (existing) { if (!mesh_->get_property_handle(prop_, propname)) { std::ostringstream oss; oss << "Requested property handle \"" << propname << "\" does not exist."; throw std::runtime_error(oss.str()); } } else { mesh_->add_property(prop_, propname); } } PropertyManager() : mesh_(0), retain_(false) { } ~PropertyManager() { deleteProperty(); } void swap(PropertyManager &rhs) { std::swap(mesh_, rhs.mesh_); std::swap(prop_, rhs.prop_); std::swap(retain_, rhs.retain_); std::swap(name_, rhs.name_); } static bool propertyExists(MeshT &mesh, const char *propname) { PROPTYPE dummy; return mesh.get_property_handle(dummy, propname); } bool isValid() const { return mesh_ != 0; } operator bool() const { return isValid(); } const PROPTYPE &getRawProperty() const { return prop_; } const std::string &getName() const { return name_; } MeshT &getMesh() const { return *mesh_; } #if (defined(_MSC_VER) && (_MSC_VER >= 1900)) || __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__) /// Only for pre C++11 compatibility. typedef PropertyManager Proxy; /** * Move constructor. Transfers ownership (delete responsibility). */ PropertyManager(PropertyManager &&rhs) : mesh_(rhs.mesh_), prop_(rhs.prop_), retain_(rhs.retain_), name_(rhs.name_) { rhs.retain_ = true; } /** * Move assignment. Transfers ownership (delete responsibility). */ PropertyManager &operator=(PropertyManager &&rhs) { if (&rhs != this) { deleteProperty(); mesh_ = rhs.mesh_; prop_ = rhs.prop_; retain_ = rhs.retain_; name_ = rhs.name_; rhs.retain_ = true; } return *this; } /** * Create a property manager for the supplied property and mesh. * If the property doesn't exist, it is created. In any case, * lifecycle management is disabled. * * @see makePropertyManagerFromExistingOrNew */ static PropertyManager createIfNotExists(MeshT &mesh, const char *propname) { PROPTYPE dummy_prop; PropertyManager pm(mesh, propname, mesh.get_property_handle(dummy_prop, propname)); pm.retain(); return std::move(pm); } /** * Like createIfNotExists() with two parameters except, if the property * doesn't exist, it is initialized with the supplied value over * the supplied range after creation. If the property already exists, * this method has the exact same effect as the two parameter version. * Lifecycle management is disabled in any case. * * @see makePropertyManagerFromExistingOrNew */ template static PropertyManager createIfNotExists(MeshT &mesh, const char *propname, const ITERATOR_TYPE &begin, const ITERATOR_TYPE &end, const PROP_VALUE &init_value) { const bool exists = propertyExists(mesh, propname); PropertyManager pm(mesh, propname, exists); pm.retain(); if (!exists) pm.set_range(begin, end, init_value); return std::move(pm); } /** * Like createIfNotExists() with two parameters except, if the property * doesn't exist, it is initialized with the supplied value over * the supplied range after creation. If the property already exists, * this method has the exact same effect as the two parameter version. * Lifecycle management is disabled in any case. * * @see makePropertyManagerFromExistingOrNew */ template static PropertyManager createIfNotExists(MeshT &mesh, const char *propname, const ITERATOR_RANGE &range, const PROP_VALUE &init_value) { return createIfNotExists( mesh, propname, range.begin(), range.end(), init_value); } PropertyManager duplicate(const char *clone_name) { PropertyManager pm(*mesh_, clone_name, false); pm.mesh_->property(pm.prop_) = mesh_->property(prop_); return pm; } /** * Included for backwards compatibility with non-C++11 version. */ PropertyManager move() { return std::move(*this); } #else class Proxy { private: Proxy(MeshT *mesh_, PROPTYPE prop_, bool retain_, const std::string &name_) : mesh_(mesh_), prop_(prop_), retain_(retain_), name_(name_) {} MeshT *mesh_; PROPTYPE prop_; bool retain_; std::string name_; friend class PropertyManager; }; operator Proxy() { Proxy p(mesh_, prop_, retain_, name_); mesh_ = 0; retain_ = true; return p; } Proxy move() { return (Proxy)*this; } PropertyManager(Proxy p) : mesh_(p.mesh_), prop_(p.prop_), retain_(p.retain_), name_(p.name_) {} PropertyManager &operator=(Proxy p) { PropertyManager(p).swap(*this); return *this; } /** * Create a property manager for the supplied property and mesh. * If the property doesn't exist, it is created. In any case, * lifecycle management is disabled. * * @see makePropertyManagerFromExistingOrNew */ static Proxy createIfNotExists(MeshT &mesh, const char *propname) { PROPTYPE dummy_prop; PropertyManager pm(mesh, propname, mesh.get_property_handle(dummy_prop, propname)); pm.retain(); return (Proxy)pm; } /** * Like createIfNotExists() with two parameters except, if the property * doesn't exist, it is initialized with the supplied value over * the supplied range after creation. If the property already exists, * this method has the exact same effect as the two parameter version. * Lifecycle management is disabled in any case. * * @see makePropertyManagerFromExistingOrNew */ template static Proxy createIfNotExists(MeshT &mesh, const char *propname, const ITERATOR_TYPE &begin, const ITERATOR_TYPE &end, const PROP_VALUE &init_value) { const bool exists = propertyExists(mesh, propname); PropertyManager pm(mesh, propname, exists); pm.retain(); if (!exists) pm.set_range(begin, end, init_value); return (Proxy)pm; } Proxy duplicate(const char *clone_name) { PropertyManager pm(*mesh_, clone_name, false); pm.mesh_->property(pm.prop_) = mesh_->property(prop_); return (Proxy)pm; } #endif /** * \brief Disable lifecycle management for this property. * * If this method is called, the encapsulated property will not be deleted * upon destruction of the PropertyManager instance. */ inline void retain(bool doRetain = true) { retain_ = doRetain; } /** * Access the encapsulated property. */ inline PROPTYPE &operator* () { return prop_; } /** * Access the encapsulated property. */ inline const PROPTYPE &operator* () const { return prop_; } /** * Enables convenient access to the encapsulated property. * * For a usage example see this class' documentation. * * @param handle A handle of the appropriate handle type. (I.e. \p VertexHandle for \p VPropHandleT, etc.) */ template inline typename PROPTYPE::reference operator[] (const HandleType &handle) { return mesh_->property(prop_, handle); } /** * Enables convenient access to the encapsulated property. * * For a usage example see this class' documentation. * * @param handle A handle of the appropriate handle type. (I.e. \p VertexHandle for \p VPropHandleT, etc.) */ template inline typename PROPTYPE::const_reference operator[] (const HandleType &handle) const { return mesh_->property(prop_, handle); } /** * Conveniently set the property for an entire range of values. * * Examples: * \code * MeshT mesh; * PropertyManager, MeshT> distance( * mesh, "distance.plugin-example.i8.informatik.rwth-aachen.de"); * distance.set_range( * mesh.vertices_begin(), mesh.vertices_end(), * std::numeric_limits::infinity()); * \endcode * or * \code * MeshT::VertexHandle vh; * distance.set_range( * mesh.vv_begin(vh), mesh.vv_end(vh), * std::numeric_limits::infinity()); * \endcode * * @param begin Start iterator. Needs to dereference to HandleType. * @param end End iterator. (Exclusive.) * @param value The value the range will be set to. */ template void set_range(HandleTypeIterator begin, HandleTypeIterator end, const PROP_VALUE &value) { for (; begin != end; ++begin) (*this)[*begin] = value; } #if (defined(_MSC_VER) && (_MSC_VER >= 1900)) || __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__) template void set_range(const HandleTypeIteratorRange &range, const PROP_VALUE &value) { set_range(range.begin(), range.end(), value); } #endif /** * Conveniently transfer the values managed by one property manager * onto the values managed by a different property manager. * * @param begin Start iterator. Needs to dereference to HandleType. Will * be used with this property manager. * @param end End iterator. (Exclusive.) Will be used with this property * manager. * @param dst_propmanager The destination property manager. * @param dst_begin Start iterator. Needs to dereference to the * HandleType of dst_propmanager. Will be used with dst_propmanager. * @param dst_end End iterator. (Exclusive.) * Will be used with dst_propmanager. Used to double check the bounds. */ template void copy_to(HandleTypeIterator begin, HandleTypeIterator end, PropertyManager &dst_propmanager, HandleTypeIterator_2 dst_begin, HandleTypeIterator_2 dst_end) const { for (; begin != end && dst_begin != dst_end; ++begin, ++dst_begin) { dst_propmanager[*dst_begin] = (*this)[*begin]; } } template void copy_to(const RangeType &range, PropertyManager &dst_propmanager, const RangeType_2 &dst_range) const { copy_to(range.begin(), range.end(), dst_propmanager, dst_range.begin(), dst_range.end()); } /** * Copy the values of a property from a source range to * a target range. The source range must not be smaller than the * target range. * * @param prop_name Name of the property to copy. Must exist on the * source mesh. Will be created on the target mesh if it doesn't exist. * * @param src_mesh Source mesh from which to copy. * @param src_range Source range which to copy. Must not be smaller than * dst_range. * @param dst_mesh Destination mesh on which to copy. * @param dst_range Destination range. */ template static void copy(const char *prop_name, MeshT &src_mesh, const RangeType &src_range, MeshT_2 &dst_mesh, const RangeType_2 &dst_range) { typedef OpenMesh::PropertyManager DstPM; DstPM dst(DstPM::createIfNotExists(dst_mesh, prop_name)); typedef OpenMesh::PropertyManager SrcPM; SrcPM src(src_mesh, prop_name, true); src.copy_to(src_range, dst, dst_range); } private: void deleteProperty() { if (!retain_) mesh_->remove_property(prop_); } private: MeshT *mesh_; PROPTYPE prop_; bool retain_; std::string name_; }; /** \relates PropertyManager * Creates a new property whose lifecycle is managed by the returned * PropertyManager. * * Intended for temporary properties. Shadows any existsing properties of * matching name and type. */ template PropertyManager makePropertyManagerFromNew(MeshT &mesh, const char *propname) { return PropertyManager(mesh, propname, false); } /** \relates PropertyManager * Creates a non-owning wrapper for an existing mesh property (no lifecycle * management). * * Intended for convenient access. * * @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. */ template PropertyManager makePropertyManagerFromExisting(MeshT &mesh, const char *propname) { return PropertyManager(mesh, propname, true); } /** \relates PropertyManager * 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) { return PropertyManager::createIfNotExists(mesh, propname); } /** \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, * this function has the exact same effect as the two parameter version. * * 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, const ITERATOR_TYPE &begin, const ITERATOR_TYPE &end, const PROP_VALUE &init_value) { return PropertyManager::createIfNotExists( mesh, propname, begin, end, init_value); } /** \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, * this function has the exact same effect as the two parameter version. * * 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, const ITERATOR_RANGE &range, const PROP_VALUE &init_value) { return makePropertyManagerFromExistingOrNew( mesh, propname, range.begin(), range.end(), init_value); } } /* namespace OpenMesh */ #endif /* PROPERTYMANAGER_HH_ */