Merge branch 'PropertyManagerRefactoring' into SmartRanges

# Conflicts:
#	src/OpenMesh/Core/Mesh/PolyConnectivity.hh
This commit is contained in:
Max Lyon
2019-11-05 13:09:07 +01:00
32 changed files with 2327 additions and 689 deletions

View File

@@ -105,10 +105,26 @@ public:
FAttribs = MeshItems::FAttribs
};
typedef VPropHandleT<VertexData> DataVPropHandle;
typedef HPropHandleT<HalfedgeData> DataHPropHandle;
typedef EPropHandleT<EdgeData> DataEPropHandle;
typedef FPropHandleT<FaceData> DataFPropHandle;
typedef VPropHandleT<VertexData> DataVPropHandle;
typedef HPropHandleT<HalfedgeData> DataHPropHandle;
typedef EPropHandleT<EdgeData> DataEPropHandle;
typedef FPropHandleT<FaceData> DataFPropHandle;
typedef VPropHandleT<Point> PointsPropertyHandle;
typedef VPropHandleT<Normal> VertexNormalsPropertyHandle;
typedef VPropHandleT<Color> VertexColorsPropertyHandle;
typedef VPropHandleT<TexCoord1D> VertexTexCoords1DPropertyHandle;
typedef VPropHandleT<TexCoord2D> VertexTexCoords2DPropertyHandle;
typedef VPropHandleT<TexCoord3D> VertexTexCoords3DPropertyHandle;
typedef HPropHandleT<TexCoord1D> HalfedgeTexCoords1DPropertyHandle;
typedef HPropHandleT<TexCoord2D> HalfedgeTexCoords2DPropertyHandle;
typedef HPropHandleT<TexCoord3D> HalfedgeTexCoords3DPropertyHandle;
typedef EPropHandleT<Color> EdgeColorsPropertyHandle;
typedef HPropHandleT<Normal> HalfedgeNormalsPropertyHandle;
typedef HPropHandleT<Color> HalfedgeColorsPropertyHandle;
typedef FPropHandleT<Normal> FaceNormalsPropertyHandle;
typedef FPropHandleT<Color> FaceColorsPropertyHandle;
typedef FPropHandleT<TextureIndex> FaceTextureIndexPropertyHandle;
public:
@@ -240,6 +256,9 @@ public:
void set_point(VertexHandle _vh, const Point& _p)
{ this->property(points_, _vh) = _p; }
PointsPropertyHandle& points_property_handle()
{ return points_; }
//------------------------------------------------------------ vertex normals
@@ -592,24 +611,6 @@ public:
bool has_face_colors() const { return face_colors_.is_valid(); }
bool has_face_texture_index() const { return face_texture_index_.is_valid(); }
public:
typedef VPropHandleT<Point> PointsPropertyHandle;
typedef VPropHandleT<Normal> VertexNormalsPropertyHandle;
typedef VPropHandleT<Color> VertexColorsPropertyHandle;
typedef VPropHandleT<TexCoord1D> VertexTexCoords1DPropertyHandle;
typedef VPropHandleT<TexCoord2D> VertexTexCoords2DPropertyHandle;
typedef VPropHandleT<TexCoord3D> VertexTexCoords3DPropertyHandle;
typedef HPropHandleT<TexCoord1D> HalfedgeTexCoords1DPropertyHandle;
typedef HPropHandleT<TexCoord2D> HalfedgeTexCoords2DPropertyHandle;
typedef HPropHandleT<TexCoord3D> HalfedgeTexCoords3DPropertyHandle;
typedef EPropHandleT<Color> EdgeColorsPropertyHandle;
typedef HPropHandleT<Normal> HalfedgeNormalsPropertyHandle;
typedef HPropHandleT<Color> HalfedgeColorsPropertyHandle;
typedef FPropHandleT<Normal> FaceNormalsPropertyHandle;
typedef FPropHandleT<Color> FaceColorsPropertyHandle;
typedef FPropHandleT<TextureIndex> FaceTextureIndexPropertyHandle;
public:
//standard vertex properties
PointsPropertyHandle points_pph() const

View File

@@ -516,7 +516,7 @@ public:
// Copy all properties, if build in is true
// Otherwise, copy only properties without build in specifier
if ( *p_it && ( _copyBuildIn || (*p_it)->name().substr(0,2) != "v:" ) )
(*p_it)->copy(_vh_from.idx(), _vh_to.idx());
(*p_it)->copy(static_cast<size_t>(_vh_from.idx()), static_cast<size_t>(_vh_to.idx()));
}
}
@@ -690,6 +690,9 @@ public: //----------------------------------------------------- element numbers
virtual size_t n_edges() const { return 0; }
virtual size_t n_faces() const { return 0; }
template <typename HandleT>
size_t n_elements() const;
protected: //------------------------------------------- synchronize properties
@@ -814,6 +817,16 @@ private:
};
template <>
inline size_t BaseKernel::n_elements<VertexHandle>() const { return n_vertices(); }
template <>
inline size_t BaseKernel::n_elements<HalfedgeHandle>() const { return n_halfedges(); }
template <>
inline size_t BaseKernel::n_elements<EdgeHandle>() const { return n_edges(); }
template <>
inline size_t BaseKernel::n_elements<FaceHandle>() const { return n_faces(); }
//=============================================================================
} // namespace OpenMesh
//=============================================================================

View File

@@ -0,0 +1,66 @@
/* ========================================================================= *
* *
* OpenMesh *
* Copyright (c) 2001-2019, 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. *
* *
* ========================================================================= */
#ifndef OPENMESH_DEFAULTPOLYMESH_HH
#define OPENMESH_DEFAULTPOLYMESH_HH
//== INCLUDES =================================================================
#include <OpenMesh/Core/Mesh/Traits.hh>
#include <OpenMesh/Core/Mesh/PolyMesh_ArrayKernelT.hh>
//== NAMESPACES ===============================================================
namespace OpenMesh {
//== TYPEDEFS =================================================================
typedef PolyMesh_ArrayKernelT<DefaultTraitsDouble> PolyMesh;
//=============================================================================
} // namespace OpenMesh
//=============================================================================
//=============================================================================
#endif // OPENMESH_DEFAULTPOLYMESH_HH defined
//=============================================================================

View File

@@ -0,0 +1,66 @@
/* ========================================================================= *
* *
* OpenMesh *
* Copyright (c) 2001-2019, 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. *
* *
* ========================================================================= */
#ifndef OPENMESH_DEFAULTTRIMESH_HH
#define OPENMESH_DEFAULTTRIMESH_HH
//== INCLUDES =================================================================
#include <OpenMesh/Core/Mesh/Traits.hh>
#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
//== NAMESPACES ===============================================================
namespace OpenMesh {
//== TYPEDEFS =================================================================
typedef TriMesh_ArrayKernelT<DefaultTraitsDouble> TriMesh;
//=============================================================================
} // namespace OpenMesh
//=============================================================================
//=============================================================================
#endif // OPENMESH_DEFAULTTRIMESH_HH defined
//=============================================================================

View File

@@ -1099,6 +1099,9 @@ public:
&PolyConnectivity::faces_end>> ConstFaceRangeSkipping;
template <typename HandleType>
struct ElementRange;
/**
* @return The vertices as a range object suitable
* for C++11 range based for loops. Will skip deleted vertices.
@@ -1147,12 +1150,26 @@ public:
*/
ConstFaceRange all_faces() const;
/**
* @return The elements corresponding to the template type as a range object suitable
* for C++11 range based for loops. Will skip deleted faces.
*/
template <typename HandleType>
typename ElementRange<HandleType>::RangeSkipping elements() const;
/**
* @return The elements corresponding to the template type as a range object suitable
* for C++11 range based for loops. Will include deleted faces.
*/
template <typename HandleType>
typename ElementRange<HandleType>::Range all_elements() const;
typedef CirculatorRange<CirculatorRangeTraitT<
PolyConnectivity,
ConstVertexVertexCWIter,
VertexHandle,
VertexHandle,
VertexHandle,
VertexHandle,
&PolyConnectivity::cvv_cwbegin,
&PolyConnectivity::cvv_cwend>> ConstVertexVertexRange;
typedef CirculatorRange<CirculatorRangeTraitT<
@@ -1524,6 +1541,34 @@ private: // Working storage for add_face()
};
template <>
struct PolyConnectivity::ElementRange<VertexHandle>
{
using Range = ConstVertexRange;
using RangeSkipping = ConstVertexRangeSkipping;
};
template <>
struct PolyConnectivity::ElementRange<HalfedgeHandle>
{
using Range = ConstHalfedgeRange;
using RangeSkipping = ConstHalfedgeRangeSkipping;
};
template <>
struct PolyConnectivity::ElementRange<EdgeHandle>
{
using Range = ConstEdgeRange;
using RangeSkipping = ConstEdgeRangeSkipping;
};
template <>
struct PolyConnectivity::ElementRange<FaceHandle>
{
using Range = ConstFaceRange;
using RangeSkipping = ConstFaceRangeSkipping;
};
}//namespace OpenMesh
@@ -1569,6 +1614,7 @@ class EntityRange : public SmartRangeT<EntityRange<RangeTraitT>, typename RangeT
typename RangeTraitT::CONTAINER_TYPE &container_;
};
/// Generic class for iterator ranges.
template <typename CirculatorRangeTraitT>
//class CirculatorRange : public SmartRangeT<CirculatorRange<CirculatorRangeTraitT>, decltype (make_smart(std::declval<typename CirculatorRangeTraitT::TO_ENTITYE_TYPE>(), std::declval<PolyConnectivity>()))>{
@@ -1592,7 +1638,6 @@ class CirculatorRange : public SmartRangeT<CirculatorRange<CirculatorRangeTraitT
CENTER_ENTITY_TYPE center_;
};
inline PolyConnectivity::ConstVertexRangeSkipping PolyConnectivity::vertices() const { return ConstVertexRangeSkipping(*this); }
inline PolyConnectivity::ConstVertexRange PolyConnectivity::all_vertices() const { return ConstVertexRange(*this); }
inline PolyConnectivity::ConstHalfedgeRangeSkipping PolyConnectivity::halfedges() const { return ConstHalfedgeRangeSkipping(*this); }
@@ -1602,6 +1647,15 @@ inline PolyConnectivity::ConstEdgeRange PolyConnectivity::all_edges(
inline PolyConnectivity::ConstFaceRangeSkipping PolyConnectivity::faces() const { return ConstFaceRangeSkipping(*this); }
inline PolyConnectivity::ConstFaceRange PolyConnectivity::all_faces() const { return ConstFaceRange(*this); }
template <> inline PolyConnectivity::ConstVertexRangeSkipping PolyConnectivity::elements<VertexHandle>() const { return vertices(); }
template <> inline PolyConnectivity::ConstVertexRange PolyConnectivity::all_elements<VertexHandle>() const { return all_vertices(); }
template <> inline PolyConnectivity::ConstHalfedgeRangeSkipping PolyConnectivity::elements<HalfedgeHandle>() const { return halfedges(); }
template <> inline PolyConnectivity::ConstHalfedgeRange PolyConnectivity::all_elements<HalfedgeHandle>() const { return all_halfedges(); }
template <> inline PolyConnectivity::ConstEdgeRangeSkipping PolyConnectivity::elements<EdgeHandle>() const { return edges(); }
template <> inline PolyConnectivity::ConstEdgeRange PolyConnectivity::all_elements<EdgeHandle>() const { return all_edges(); }
template <> inline PolyConnectivity::ConstFaceRangeSkipping PolyConnectivity::elements<FaceHandle>() const { return faces(); }
template <> inline PolyConnectivity::ConstFaceRange PolyConnectivity::all_elements<FaceHandle>() const { return all_faces(); }
inline PolyConnectivity::ConstVertexVertexRange PolyConnectivity::vv_range(VertexHandle _vh) const {
return ConstVertexVertexRange(*this, _vh);
}

View File

@@ -152,6 +152,24 @@ struct DefaultTraits
FaceAttributes(0);
};
/** \class DefaultTraitsDouble Traits.hh <OpenMesh/Mesh/Traits.hh>
Version of Default Traits that uses double precision for points and
normals as well as floating point vectors for colors
\see The Mesh docu section on \ref mesh_type.
\see Traits.hh for a list of macros for traits classes.
*/
struct DefaultTraitsDouble : public DefaultTraits
{
/// Use double precision points
typedef OpenMesh::Vec3d Point;
/// Use double precision Normals
typedef OpenMesh::Vec3d Normal;
/// Use RGBA Color
typedef OpenMesh::Vec4f Color;
};
//== CLASS DEFINITION =========================================================

View File

@@ -485,6 +485,7 @@ struct VPropHandleT : public BasePropHandleT<T>
{
typedef T Value;
typedef T value_type;
typedef VertexHandle Handle;
explicit VPropHandleT(int _idx=-1) : BasePropHandleT<T>(_idx) {}
explicit VPropHandleT(const BasePropHandleT<T>& _b) : BasePropHandleT<T>(_b) {}
@@ -499,6 +500,7 @@ struct HPropHandleT : public BasePropHandleT<T>
{
typedef T Value;
typedef T value_type;
typedef HalfedgeHandle Handle;
explicit HPropHandleT(int _idx=-1) : BasePropHandleT<T>(_idx) {}
explicit HPropHandleT(const BasePropHandleT<T>& _b) : BasePropHandleT<T>(_b) {}
@@ -513,6 +515,7 @@ struct EPropHandleT : public BasePropHandleT<T>
{
typedef T Value;
typedef T value_type;
typedef EdgeHandle Handle;
explicit EPropHandleT(int _idx=-1) : BasePropHandleT<T>(_idx) {}
explicit EPropHandleT(const BasePropHandleT<T>& _b) : BasePropHandleT<T>(_b) {}
@@ -527,6 +530,7 @@ struct FPropHandleT : public BasePropHandleT<T>
{
typedef T Value;
typedef T value_type;
typedef FaceHandle Handle;
explicit FPropHandleT(int _idx=-1) : BasePropHandleT<T>(_idx) {}
explicit FPropHandleT(const BasePropHandleT<T>& _b) : BasePropHandleT<T>(_b) {}
@@ -541,11 +545,39 @@ struct MPropHandleT : public BasePropHandleT<T>
{
typedef T Value;
typedef T value_type;
typedef void Handle;
explicit MPropHandleT(int _idx=-1) : BasePropHandleT<T>(_idx) {}
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
//=============================================================================
#endif // OPENMESH_PROPERTY_HH defined

View File

@@ -44,6 +44,7 @@
#include <OpenMesh/Core/System/config.h>
#include <OpenMesh/Core/Utils/HandleToPropHandle.hh>
#include <OpenMesh/Core/Mesh/PolyConnectivity.hh>
#include <sstream>
#include <stdexcept>
#include <string>
@@ -59,6 +60,8 @@ namespace OpenMesh {
* makeTemporaryProperty(), getProperty(), and getOrMakeProperty()
* to construct a PropertyManager, e.g.
*
* Note that the second template parameter is depcretated.
*
* \code
* {
* TriMesh mesh;
@@ -73,27 +76,54 @@ namespace OpenMesh {
* }
* \endcode
*/
template<typename PROPTYPE, typename MeshT>
template<typename PROPTYPE, typename MeshT = int>
class PropertyManager {
#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__)
public:
PropertyManager(const PropertyManager&) = delete;
PropertyManager& operator=(const PropertyManager&) = delete;
#else
using Value = typename PROPTYPE::Value;
using value_type = typename PROPTYPE::value_type;
using Handle = typename PROPTYPE::Handle;
using Self = PropertyManager<PROPTYPE, MeshT>;
private:
/**
* Noncopyable because there aren't no straightforward copy semantics.
*/
PropertyManager(const PropertyManager&);
// 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;
/**
* Noncopyable because there aren't no straightforward copy semantics.
*/
PropertyManager& operator=(const PropertyManager&);
#endif
// 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.
*
* Constructor.
*
* Throws an \p std::runtime_error if \p existing is true and
@@ -107,22 +137,114 @@ class PropertyManager {
* the instance merely acts as a convenience wrapper around an existing property with no
* lifecycle management whatsoever.
*
* @see PropertyManager::createIfNotExists, makePropertyManagerFromNew,
* makePropertyManagerFromExisting, makePropertyManagerFromExistingOrNew
* @see PropertyManager::getOrMakeProperty, PropertyManager::getProperty,
* PropertyManager::makeTemporaryProperty
*/
PropertyManager(MeshT &mesh, const char *propname, bool existing = false) : mesh_(&mesh), retain_(existing), name_(propname) {
OM_DEPRECATED("Use the constructor without parameter 'existing' instead. Check for existance with hasProperty") // As long as this overload exists, initial value must be first parameter due to ambiguity for properties of type bool
PropertyManager(PolyConnectivity& mesh, const char *propname, bool existing) : mesh_(mesh), retain_(existing), name_(propname) {
if (existing) {
if (!mesh_->get_property_handle(prop_, propname)) {
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);
mesh_.add_property(prop_, propname);
}
}
PropertyManager() : mesh_(0), retain_(false) {
/**
* Constructor.
*
* Asks for a property with name propname and creates one if none exists. Lifetime is not managed.
*
* @param mesh The mesh on which to create the property.
* @param propname The name of the property.
*/
PropertyManager(PolyConnectivity& mesh, const char *propname) : mesh_(mesh), retain_(true), name_(propname) {
if (!mesh_.get_property_handle(prop_, propname)) {
mesh_.add_property(prop_, propname);
}
}
/**
* Constructor.
*
* Asks for a property with name propname and creates one if none exists. Lifetime is not managed.
*
* @param initial_value If the proeprty is newly created, it will be initialized with intial_value.
* If the property already existed, nothing is changes.
* @param mesh The mesh on which to create 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) {
if (!mesh_.get_property_handle(prop_, propname)) {
mesh_.add_property(prop_, propname);
set_range(mesh_.all_elements<Handle>(), intial_value);
}
}
/**
* Constructor.
*
* Create an anonymous property. Lifetime is managed.
*
* @param mesh The mesh on which to create the property.
*/
PropertyManager(PolyConnectivity& mesh) : mesh_(mesh), retain_(false), name_("") {
mesh_.add_property(prop_, name_);
}
/**
* Constructor.
*
* Create an anonymous property. Lifetime is managed.
*
* @param initial_value The property will be initialized with intial_value.
* @param mesh The mesh on which to create the property.
*/
PropertyManager(const Value& intial_value, PolyConnectivity& mesh) : mesh_(mesh), retain_(false), name_("") {
mesh_.add_property(prop_, name_);
set_range(mesh_.all_elements<Handle>(), intial_value);
}
/**
* Constructor.
*
* Create a wrapper around an existing property. Lifetime is not managed.
*
* @param mesh The mesh on which to create the property.
*/
PropertyManager(PolyConnectivity& mesh, PROPTYPE property_handle) : mesh_(mesh), prop_(property_handle), retain_(true), name_() {
}
PropertyManager() = delete;
PropertyManager(const PropertyManager& rhs)
:
mesh_(rhs.mesh_),
prop_(),
retain_(rhs.retain_),
name_(rhs.name_)
{
if (rhs.retain_) // named property -> create a property manager referring to the same
{
prop_ = rhs.prop_;
}
else // unnamed property -> create a property manager refering to a new property and copy the contents
{
mesh_.add_property(prop_, name_);
Storage::copy(rhs, *this);
}
}
PropertyManager& operator=(const PropertyManager& rhs)
{
if (&mesh_ == &rhs.mesh_ && prop_ == rhs.prop_)
; // nothing to do
else
Storage::copy(rhs, *this);
return *this;
}
~PropertyManager() {
@@ -130,49 +252,75 @@ class PropertyManager {
}
void swap(PropertyManager &rhs) {
std::swap(mesh_, rhs.mesh_);
std::swap(prop_, rhs.prop_);
std::swap(retain_, rhs.retain_);
std::swap(name_, rhs.name_);
// swap the data stored in the properties
Storage::swap(rhs, *this);
}
static bool propertyExists(MeshT &mesh, const char *propname) {
static bool propertyExists(PolyConnectivity &mesh, const char *propname) {
PROPTYPE dummy;
return mesh.get_property_handle(dummy, propname);
}
bool isValid() const { return mesh_ != 0; }
bool isValid() const { return prop_.is_valid(); }
operator bool() const { return isValid(); }
const PROPTYPE &getRawProperty() const { return prop_; }
const std::string &getName() const { return name_; }
MeshT &getMesh() const { return *mesh_; }
/**
* Get the mesh corresponding to the property.
*
* If you use PropertyManager without second template parameter (recommended)
* you need to specify the actual mesh type when using this function, e.g.:
* \code
* {
* TriMesh mesh;
* auto visited = VProp<bool>(mesh);
* TriMesh& mesh_ref = visited.getMesh<TriMesh>();
* }
*
*/
template <typename MeshType >
MeshType& getMesh() const { return dynamic_cast<MeshType&>(mesh_); }
#if (defined(_MSC_VER) && (_MSC_VER >= 1800)) || __cplusplus > 199711L || defined(__GXX_EXPERIMENTAL_CXX0X__)
/// Only for pre C++11 compatibility.
typedef PropertyManager<PROPTYPE, MeshT> Proxy;
MeshT& getMesh() const { return dynamic_cast<MeshT&>(mesh_); }
/**
* Move constructor. Transfers ownership (delete responsibility).
*/
PropertyManager(PropertyManager &&rhs) : mesh_(rhs.mesh_), prop_(rhs.prop_), retain_(rhs.retain_), name_(rhs.name_) {
rhs.retain_ = true;
PropertyManager(PropertyManager &&rhs)
:
mesh_(rhs.mesh_),
prop_(rhs.prop_),
retain_(rhs.retain_),
name_(rhs.name_)
{
if (!rhs.retain_)
rhs.prop_.invalidate(); // only invalidate unnamed properties
}
/**
* 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;
PropertyManager& operator=(PropertyManager&& rhs)
{
if ((&mesh_ != &rhs.mesh_) || (prop_ != rhs.prop_))
{
if (rhs.retain_)
{
// retained properties cannot be invalidated. Copy instead
Storage::copy(rhs, *this);
}
else
{
// swap the data stored in the properties
Storage::swap(rhs, *this);
// remove the property from rhs
rhs.mesh_.remove_property(rhs.prop_);
// invalidate prop_
rhs.prop_.invalidate();
}
}
return *this;
}
@@ -184,11 +332,8 @@ class PropertyManager {
*
* @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);
static PropertyManager createIfNotExists(PolyConnectivity &mesh, const char *propname) {
return PropertyManager(mesh, propname);
}
/**
@@ -201,7 +346,7 @@ class PropertyManager {
* @see makePropertyManagerFromExistingOrNew
*/
template<typename PROP_VALUE, typename ITERATOR_TYPE>
static PropertyManager createIfNotExists(MeshT &mesh, const char *propname,
static PropertyManager createIfNotExists(PolyConnectivity &mesh, const char *propname,
const ITERATOR_TYPE &begin, const ITERATOR_TYPE &end,
const PROP_VALUE &init_value) {
const bool exists = propertyExists(mesh, propname);
@@ -222,15 +367,15 @@ class PropertyManager {
* @see makePropertyManagerFromExistingOrNew
*/
template<typename PROP_VALUE, typename ITERATOR_RANGE>
static PropertyManager createIfNotExists(MeshT &mesh, const char *propname,
static PropertyManager createIfNotExists(PolyConnectivity &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_);
PropertyManager pm(mesh_, clone_name, false);
pm.mesh_.property(pm.prop_) = mesh_.property(prop_);
return pm;
}
@@ -241,89 +386,6 @@ class PropertyManager {
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<typename PROP_VALUE, typename ITERATOR_TYPE>
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 value of the encapsulated mesh property.
*
@@ -337,7 +399,7 @@ class PropertyManager {
* @note This method is only used for mesh properties.
*/
typename PROPTYPE::reference& operator*() {
return mesh_->mproperty(prop_)[0];
return mesh_.mproperty(prop_)[0];
}
/**
@@ -353,7 +415,7 @@ class PropertyManager {
* @note This method is only used for mesh properties.
*/
typename PROPTYPE::const_reference& operator*() const {
return mesh_->mproperty(prop_)[0];
return mesh_.mproperty(prop_)[0];
}
/**
@@ -365,7 +427,7 @@ class PropertyManager {
*/
template<typename HandleType>
inline typename PROPTYPE::reference operator[] (const HandleType &handle) {
return mesh_->property(prop_, handle);
return mesh_.property(prop_, handle);
}
/**
@@ -377,7 +439,7 @@ class PropertyManager {
*/
template<typename HandleType>
inline typename PROPTYPE::const_reference operator[] (const HandleType &handle) const {
return mesh_->property(prop_, handle);
return mesh_.property(prop_, handle);
}
/**
@@ -389,7 +451,7 @@ class PropertyManager {
*/
template<typename HandleType>
inline typename PROPTYPE::reference operator() (const HandleType &handle) {
return mesh_->property(prop_, handle);
return mesh_.property(prop_, handle);
}
/**
@@ -401,7 +463,7 @@ class PropertyManager {
*/
template<typename HandleType>
inline typename PROPTYPE::const_reference operator() (const HandleType &handle) const {
return mesh_->property(prop_, handle);
return mesh_.property(prop_, handle);
}
/**
@@ -410,7 +472,7 @@ class PropertyManager {
* Examples:
* \code
* MeshT mesh;
* PropertyManager<VPropHandleT<double>, MeshT> distance(
* PropertyManager<VPropHandleT<double>> distance(
* mesh, "distance.plugin-example.i8.informatik.rwth-aachen.de");
* distance.set_range(
* mesh.vertices_begin(), mesh.vertices_end(),
@@ -458,9 +520,9 @@ class PropertyManager {
* Will be used with dst_propmanager. Used to double check the bounds.
*/
template<typename HandleTypeIterator, typename PROPTYPE_2,
typename MeshT_2, typename HandleTypeIterator_2>
typename HandleTypeIterator_2>
void copy_to(HandleTypeIterator begin, HandleTypeIterator end,
PropertyManager<PROPTYPE_2, MeshT_2> &dst_propmanager,
PropertyManager<PROPTYPE_2> &dst_propmanager,
HandleTypeIterator_2 dst_begin, HandleTypeIterator_2 dst_end) const {
for (; begin != end && dst_begin != dst_end; ++begin, ++dst_begin) {
@@ -469,14 +531,15 @@ class PropertyManager {
}
template<typename RangeType, typename PROPTYPE_2,
typename MeshT_2, typename RangeType_2>
typename RangeType_2>
void copy_to(const RangeType &range,
PropertyManager<PROPTYPE_2, MeshT_2> &dst_propmanager,
PropertyManager<PROPTYPE_2> &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
@@ -491,15 +554,15 @@ class PropertyManager {
* @param dst_mesh Destination mesh on which to copy.
* @param dst_range Destination range.
*/
template<typename RangeType, typename MeshT_2, typename RangeType_2>
template<typename RangeType, typename RangeType_2>
static void copy(const char *prop_name,
MeshT &src_mesh, const RangeType &src_range,
MeshT_2 &dst_mesh, const RangeType_2 &dst_range) {
PolyConnectivity &src_mesh, const RangeType &src_range,
PolyConnectivity &dst_mesh, const RangeType_2 &dst_range) {
typedef OpenMesh::PropertyManager<PROPTYPE, MeshT> DstPM;
typedef OpenMesh::PropertyManager<PROPTYPE> DstPM;
DstPM dst(DstPM::createIfNotExists(dst_mesh, prop_name));
typedef OpenMesh::PropertyManager<PROPTYPE, MeshT_2> SrcPM;
typedef OpenMesh::PropertyManager<PROPTYPE> SrcPM;
SrcPM src(src_mesh, prop_name, true);
src.copy_to(src_range, dst, dst_range);
@@ -507,18 +570,20 @@ class PropertyManager {
private:
void deleteProperty() {
if (!retain_)
mesh_->remove_property(prop_);
if (!retain_ && prop_.is_valid())
mesh_.remove_property(prop_);
}
private:
MeshT *mesh_;
PolyConnectivity& mesh_;
PROPTYPE prop_;
bool retain_;
std::string name_;
};
/** @relates PropertyManager
*
* @deprecated Temporary properties should not have a name.
*
* Creates a new property whose lifetime is limited to the current scope.
*
@@ -541,13 +606,72 @@ class PropertyManager {
* @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);
template<typename ElementT, typename T>
PropertyManager<typename HandleToPropHandle<ElementT, T>::type>
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);
}
/** @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
* @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);
}
/** @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
@@ -575,13 +699,18 @@ makeTemporaryProperty(MeshT &mesh, const char *propname = "") {
* @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);
template<typename ElementT, typename T>
PropertyManager<typename HandleToPropHandle<ElementT, T>::type>
getProperty(PolyConnectivity &mesh, const char *propname) {
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
@@ -611,41 +740,12 @@ getProperty(MeshT &mesh, const char *propname) {
* @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
*
* 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, typename MeshT>
bool
hasProperty(const MeshT &mesh, const char *propname) {
typename HandleToPropHandle<ElementT, T>::type ph;
return mesh.get_property_handle(ph, propname);
template<typename ElementT, typename T>
PropertyManager<typename HandleToPropHandle<ElementT, T>::type>
getOrMakeProperty(PolyConnectivity &mesh, const char *propname) {
return PropertyManager<typename HandleToPropHandle<ElementT, T>::type>::createIfNotExists(mesh, propname);
}
/** @relates PropertyManager
@@ -657,11 +757,11 @@ hasProperty(const MeshT &mesh, const char *propname) {
* Intended for temporary properties. Shadows any existing properties of
* matching name and type.
*/
template<typename PROPTYPE, typename MeshT>
template<typename PROPTYPE>
OM_DEPRECATED("Use makeTemporaryProperty instead.")
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromNew(MeshT &mesh, const char *propname)
PropertyManager<PROPTYPE> makePropertyManagerFromNew(PolyConnectivity &mesh, const char *propname)
{
return PropertyManager<PROPTYPE, MeshT>(mesh, propname, false);
return PropertyManager<PROPTYPE>(mesh, propname, false);
}
/** \relates PropertyManager
@@ -676,11 +776,11 @@ PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromNew(MeshT &mesh, const c
* @throws std::runtime_error if no property with the name \p propname of
* matching type exists.
*/
template<typename PROPTYPE, typename MeshT>
template<typename PROPTYPE>
OM_DEPRECATED("Use getProperty instead.")
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExisting(MeshT &mesh, const char *propname)
PropertyManager<PROPTYPE> makePropertyManagerFromExisting(PolyConnectivity &mesh, const char *propname)
{
return PropertyManager<PROPTYPE, MeshT>(mesh, propname, true);
return PropertyManager<PROPTYPE>(mesh, propname, true);
}
/** @relates PropertyManager
@@ -691,11 +791,11 @@ PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExisting(MeshT &mesh, co
*
* Intended for creating or accessing persistent properties.
*/
template<typename PROPTYPE, typename MeshT>
template<typename PROPTYPE>
OM_DEPRECATED("Use getOrMakeProperty instead.")
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExistingOrNew(MeshT &mesh, const char *propname)
PropertyManager<PROPTYPE> makePropertyManagerFromExistingOrNew(PolyConnectivity &mesh, const char *propname)
{
return PropertyManager<PROPTYPE, MeshT>::createIfNotExists(mesh, propname);
return PropertyManager<PROPTYPE>::createIfNotExists(mesh, propname);
}
/** @relates PropertyManager
@@ -709,14 +809,14 @@ PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExistingOrNew(MeshT &mes
*
* Intended for creating or accessing persistent properties.
*/
template<typename PROPTYPE, typename MeshT,
template<typename PROPTYPE,
typename ITERATOR_TYPE, typename PROP_VALUE>
OM_DEPRECATED("Use getOrMakeProperty instead.")
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExistingOrNew(
MeshT &mesh, const char *propname,
PropertyManager<PROPTYPE> makePropertyManagerFromExistingOrNew(
PolyConnectivity &mesh, const char *propname,
const ITERATOR_TYPE &begin, const ITERATOR_TYPE &end,
const PROP_VALUE &init_value) {
return PropertyManager<PROPTYPE, MeshT>::createIfNotExists(
return PropertyManager<PROPTYPE>::createIfNotExists(
mesh, propname, begin, end, init_value);
}
@@ -731,16 +831,45 @@ PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExistingOrNew(
*
* Intended for creating or accessing persistent properties.
*/
template<typename PROPTYPE, typename MeshT,
template<typename PROPTYPE,
typename ITERATOR_RANGE, typename PROP_VALUE>
OM_DEPRECATED("Use getOrMakeProperty instead.")
PropertyManager<PROPTYPE, MeshT> makePropertyManagerFromExistingOrNew(
MeshT &mesh, const char *propname,
PropertyManager<PROPTYPE> makePropertyManagerFromExistingOrNew(
PolyConnectivity &mesh, const char *propname,
const ITERATOR_RANGE &range,
const PROP_VALUE &init_value) {
return makePropertyManagerFromExistingOrNew<PROPTYPE, MeshT>(
return makePropertyManagerFromExistingOrNew<PROPTYPE>(
mesh, propname, range.begin(), range.end(), init_value);
}
/** @relates PropertyManager
* Returns a convenience wrapper around the points property of a mesh.
*/
template<typename MeshT>
PropertyManager<OpenMesh::VPropHandleT<typename MeshT::Point>>
getPointsProperty(MeshT &mesh) {
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>
using VProp = PropertyManager<OpenMesh::VPropHandleT<T>>;
template <typename T>
using HProp = PropertyManager<OpenMesh::HPropHandleT<T>>;
template <typename T>
using EProp = PropertyManager<OpenMesh::EPropHandleT<T>>;
template <typename T>
using FProp = PropertyManager<OpenMesh::FPropHandleT<T>>;
template <typename T>
using MProp = PropertyManager<OpenMesh::MPropHandleT<T>>;
} /* namespace OpenMesh */
#endif /* PROPERTYMANAGER_HH_ */

View File

@@ -51,8 +51,8 @@ protected: // SubdividerT interface
_m.request_edge_status();
_m.request_vertex_status();
_m.request_face_status();
PropertyManager<EPropHandleT<typename mesh_t::VertexHandle>, mesh_t> edge_midpoint(_m, "edge_midpoint");
PropertyManager<VPropHandleT<bool>, mesh_t> is_original_vertex(_m, "is_original_vertex");
PropertyManager<EPropHandleT<typename mesh_t::VertexHandle>> edge_midpoint(_m, "edge_midpoint");
PropertyManager<VPropHandleT<bool>> is_original_vertex(_m, "is_original_vertex");
for (size_t iteration = 0; iteration < _n; ++iteration) {
is_original_vertex.set_range(_m.vertices_begin(), _m.vertices_end(), true);

View File

@@ -41,20 +41,25 @@ if ( OPENMESH_BUILD_UNIT_TESTS )
# Create unittest executable
acg_add_executable(unittests ${UNITTEST_SRC})
acg_add_executable(unittests_customvec ${UNITTEST_SRC})
acg_add_executable(unittests_doublevec ${UNITTEST_SRC})
target_compile_definitions(unittests_customvec PRIVATE TEST_CUSTOM_TRAITS)
target_compile_definitions(unittests_doublevec PRIVATE TEST_DOUBLE_TRAITS)
# For the unittest we don't want the install rpath as set by acg_add_executable
set_target_properties ( unittests PROPERTIES BUILD_WITH_INSTALL_RPATH 0 )
set_target_properties ( unittests PROPERTIES BUILD_WITH_INSTALL_RPATH 0 )
set_target_properties ( unittests_customvec PROPERTIES BUILD_WITH_INSTALL_RPATH 0 )
set_target_properties ( unittests_doublevec PROPERTIES BUILD_WITH_INSTALL_RPATH 0 )
# Set output directory to ${BINARY_DIR}/Unittests
set (OUTPUT_DIR "${CMAKE_BINARY_DIR}/Unittests")
set_target_properties(unittests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR})
set_target_properties(unittests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR})
set_target_properties(unittests_customvec PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR})
set_target_properties(unittests_doublevec PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR})
foreach(CONFIG ${CMAKE_CONFIGURATION_TYPES})
string(TOUPPER ${CONFIG} UPCONFIG)
set_target_properties(unittests PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${UPCONFIG} ${OUTPUT_DIR})
set_target_properties(unittests_customvec PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${UPCONFIG} ${OUTPUT_DIR})
set_target_properties(unittests_doublevec PROPERTIES RUNTIME_OUTPUT_DIRECTORY_${UPCONFIG} ${OUTPUT_DIR})
endforeach()
@@ -62,6 +67,7 @@ if ( OPENMESH_BUILD_UNIT_TESTS )
# Link against all necessary libraries
target_link_libraries(unittests OpenMeshCore OpenMeshTools ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES} pthread)
target_link_libraries(unittests_customvec OpenMeshCore OpenMeshTools ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES} pthread)
target_link_libraries(unittests_doublevec OpenMeshCore OpenMeshTools ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES} pthread)
@@ -71,8 +77,9 @@ if ( OPENMESH_BUILD_UNIT_TESTS )
add_definitions( -DOPENMESHDLL )
endif()
target_link_libraries(unittests OpenMeshCore OpenMeshTools ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES})
target_link_libraries(unittests OpenMeshCore OpenMeshTools ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES})
target_link_libraries(unittests_customvec OpenMeshCore OpenMeshTools ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES})
target_link_libraries(unittests_doublevec OpenMeshCore OpenMeshTools ${GTEST_LIBRARIES} ${GTEST_MAIN_LIBRARIES})
endif()
@@ -81,10 +88,12 @@ if ( OPENMESH_BUILD_UNIT_TESTS )
# Set compiler flags
set_target_properties(unittests PROPERTIES COMPILE_FLAGS "-g -pedantic -Wno-long-long")
set_target_properties(unittests_customvec PROPERTIES COMPILE_FLAGS "-g -pedantic -Wno-long-long")
set_target_properties(unittests_doublevec PROPERTIES COMPILE_FLAGS "-g -pedantic -Wno-long-long")
else()
# Set compiler flags
set_target_properties(unittests PROPERTIES COMPILE_FLAGS "" )
set_target_properties(unittests_customvec PROPERTIES COMPILE_FLAGS "" )
set_target_properties(unittests_doublevec PROPERTIES COMPILE_FLAGS "" )
endif()
if ( OPENMESH_BUILD_SHARED )
@@ -103,14 +112,21 @@ if ( OPENMESH_BUILD_UNIT_TESTS )
"$<TARGET_FILE:${TAR}>"
"${CMAKE_BINARY_DIR}/Unittests/$<TARGET_FILE_NAME:${TAR}>"
COMMENT "Copying OpenMesh targets to unittests directory")
add_custom_command(TARGET unittests_doublevec POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E copy
"$<TARGET_FILE:${TAR}>"
"${CMAKE_BINARY_DIR}/Unittests/$<TARGET_FILE_NAME:${TAR}>"
COMMENT "Copying OpenMesh targets to unittests directory")
endforeach(TAR)
endif()
acg_copy_after_build(unittests ${CMAKE_CURRENT_SOURCE_DIR}/TestFiles ${CMAKE_BINARY_DIR}/Unittests/)
acg_copy_after_build(unittests_customvec ${CMAKE_CURRENT_SOURCE_DIR}/TestFiles ${CMAKE_BINARY_DIR}/Unittests/)
acg_copy_after_build(unittests_doublevec ${CMAKE_CURRENT_SOURCE_DIR}/TestFiles ${CMAKE_BINARY_DIR}/Unittests/)
add_test(NAME AllTestsIn_OpenMesh_tests WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/Unittests" COMMAND "${CMAKE_BINARY_DIR}/Unittests/unittests")
add_test(NAME AllTestsIn_OpenMesh_tests_with_minimal_vector WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/Unittests" COMMAND "${CMAKE_BINARY_DIR}/Unittests/unittests_customvec")
add_test(NAME AllTestsIn_OpenMesh_tests_with_double_vector WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/Unittests" COMMAND "${CMAKE_BINARY_DIR}/Unittests/unittests_doublevec")
else(GTEST_FOUND)
message(STATUS "Google testing framework was not found, unittests disabled.")

View File

@@ -62,7 +62,7 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) {
{
OpenMesh::PropertyManager<
OpenMesh::VPropHandleT<bool>, Mesh> pm_v_bool(mesh_, "pm_v_bool");
OpenMesh::VPropHandleT<bool>> pm_v_bool(mesh_, "pm_v_bool");
pm_v_bool.set_range(mesh_.vertices_begin(), mesh_.vertices_end(), false);
for (int i = 0; i < 4; ++i)
ASSERT_FALSE(pm_v_bool[vhandle[i]]);
@@ -71,7 +71,7 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) {
ASSERT_TRUE(pm_v_bool[vhandle[i]]);
OpenMesh::PropertyManager<
OpenMesh::EPropHandleT<bool>, Mesh> pm_e_bool(mesh_, "pm_e_bool");
OpenMesh::EPropHandleT<bool>> pm_e_bool(mesh_, "pm_e_bool");
pm_e_bool.set_range(mesh_.edges_begin(), mesh_.edges_end(), false);
for (Mesh::EdgeIter e_it = mesh_.edges_begin(), f_end = mesh_.edges_end();
e_it != f_end; ++e_it)
@@ -82,7 +82,7 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) {
ASSERT_TRUE(pm_e_bool[*e_it]);
OpenMesh::PropertyManager<
OpenMesh::FPropHandleT<bool>, Mesh> pm_f_bool(mesh_, "pm_f_bool");
OpenMesh::FPropHandleT<bool>> pm_f_bool(mesh_, "pm_f_bool");
pm_f_bool.set_range(mesh_.faces_begin(), mesh_.faces_end(), false);
for (Mesh::FaceIter f_it = mesh_.faces_begin(), f_end = mesh_.faces_end();
f_it != f_end; ++f_it)
@@ -98,7 +98,7 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) {
*/
{
OpenMesh::PropertyManager<
OpenMesh::VPropHandleT<bool>, Mesh> pm_v_bool(mesh_, "pm_v_bool2");
OpenMesh::VPropHandleT<bool>> pm_v_bool(mesh_, "pm_v_bool2");
pm_v_bool.set_range(mesh_.vertices(), false);
for (int i = 0; i < 4; ++i)
ASSERT_FALSE(pm_v_bool[vhandle[i]]);
@@ -107,7 +107,7 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) {
ASSERT_TRUE(pm_v_bool[vhandle[i]]);
OpenMesh::PropertyManager<
OpenMesh::EPropHandleT<bool>, Mesh> pm_e_bool(mesh_, "pm_e_bool2");
OpenMesh::EPropHandleT<bool>> pm_e_bool(mesh_, "pm_e_bool2");
pm_e_bool.set_range(mesh_.edges(), false);
for (Mesh::EdgeIter e_it = mesh_.edges_begin(), f_end = mesh_.edges_end();
e_it != f_end; ++e_it)
@@ -118,7 +118,7 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) {
ASSERT_TRUE(pm_e_bool[*e_it]);
OpenMesh::PropertyManager<
OpenMesh::FPropHandleT<bool>, Mesh> pm_f_bool(mesh_, "pm_f_bool2");
OpenMesh::FPropHandleT<bool>> pm_f_bool(mesh_, "pm_f_bool2");
pm_f_bool.set_range(mesh_.faces(), false);
for (Mesh::FaceIter f_it = mesh_.faces_begin(), f_end = mesh_.faces_end();
f_it != f_end; ++f_it)
@@ -130,62 +130,6 @@ TEST_F(OpenMeshPropertyManager, set_range_bool) {
}
}
/*
* ====================================================================
* Factory Functions
* ====================================================================
*/
template<typename PropHandle, typename Mesh>
bool has_property(const Mesh& _mesh, const std::string& _name) {
auto dummy_handle = PropHandle{};
return _mesh.get_property_handle(dummy_handle, _name);
}
/*
* Temporary property
*/
TEST_F(OpenMeshPropertyManager, cpp11_temp_property) {
using handle_type = OpenMesh::VPropHandleT<int>;
const auto prop_name = "pm_v_test_property";
ASSERT_FALSE(has_property<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));
}
ASSERT_FALSE(has_property<handle_type>(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
using handle_type = OpenMesh::VPropHandleT<int>;
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(has_property<handle_type>(mesh_, prop_name));
ASSERT_EQ(100, outer_prop[vh]);
}
/*
* In sequence:
* - add a persistent property to a mesh
@@ -196,10 +140,9 @@ TEST_F(OpenMeshPropertyManager, cpp11_temp_property_shadowing) {
TEST_F(OpenMeshPropertyManager, cpp11_persistent_and_non_owning_properties) {
auto vh = mesh_.add_vertex({0,0,0}); // Dummy vertex to attach properties to
using handle_type = OpenMesh::VPropHandleT<int>;
const auto prop_name = "pm_v_test_property";
ASSERT_FALSE(has_property<handle_type>(mesh_, prop_name));
ASSERT_FALSE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
{
auto prop = OpenMesh::getOrMakeProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name);
@@ -207,7 +150,7 @@ TEST_F(OpenMeshPropertyManager, cpp11_persistent_and_non_owning_properties) {
// End of scope, property persists
}
ASSERT_TRUE(has_property<handle_type>(mesh_, prop_name));
ASSERT_TRUE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
{
// Since a property of the same name and type already exists, this refers to the existing property.
@@ -217,7 +160,7 @@ TEST_F(OpenMeshPropertyManager, cpp11_persistent_and_non_owning_properties) {
// End of scope, property persists
}
ASSERT_TRUE(has_property<handle_type>(mesh_, prop_name));
ASSERT_TRUE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
{
// Acquire non-owning handle to the property, knowing it exists
@@ -225,7 +168,7 @@ TEST_F(OpenMeshPropertyManager, cpp11_persistent_and_non_owning_properties) {
ASSERT_EQ(200, prop[vh]);
}
ASSERT_TRUE(has_property<handle_type>(mesh_, prop_name));
ASSERT_TRUE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
{
// Attempt to acquire non-owning handle for a non-existing property
@@ -235,7 +178,630 @@ TEST_F(OpenMeshPropertyManager, cpp11_persistent_and_non_owning_properties) {
ASSERT_THROW(code_that_throws(), std::runtime_error);
}
ASSERT_TRUE(has_property<handle_type>(mesh_, prop_name));
ASSERT_TRUE((OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, prop_name)));
}
TEST_F(OpenMeshPropertyManager, property_copy_construction) {
for (int i = 0; i < 1000000; ++i)
mesh_.add_vertex(Mesh::Point());
// unnamed
{
auto prop1 = OpenMesh::VProp<int>(mesh_);
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
auto prop2 = prop1; // prop1 and prop2 should be two different properties with the same content
prop1.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13);
}
// named
{
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids");
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
auto prop2 = prop1; // prop1 and prop2 should refere to the same property
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13);
prop1.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0);
}
}
TEST_F(OpenMeshPropertyManager, property_move_construction) {
for (int i = 0; i < 1000000; ++i)
mesh_.add_vertex(Mesh::Point());
// unnamed
{
auto prop1 = OpenMesh::VProp<int>(mesh_);
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
auto t_start = std::chrono::high_resolution_clock::now();
auto prop2 = std::move(prop1);
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "move constructing property from temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_FALSE(prop1.isValid()) << "prop1 should have been invalidated";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13);
}
// named
{
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids");
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
auto t_start = std::chrono::high_resolution_clock::now();
auto prop2 = std::move(prop1); // prop1 and prop2 should refere to the same property
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "move constructing from named took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_TRUE(prop1.isValid()) << "named properties cannot be invalidated";
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "property is not valid anymore";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "did not copy property correctly";
prop1.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0);
}
}
TEST_F(OpenMeshPropertyManager, property_copying_same_mesh) {
for (int i = 0; i < 1000000; ++i)
mesh_.add_vertex(Mesh::Point());
// unnamed to unnamed
{
auto prop1 = OpenMesh::VProp<int>(3, mesh_);
auto prop2 = OpenMesh::VProp<int>(0, mesh_);
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 3) << "Property not initialized correctly";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = prop1;
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "copying property temporary to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Temporary property got destroyed";
prop1.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 0) << "Property not copied correctly";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
}
// unnamed to named
{
auto prop1 = OpenMesh::VProp<int>(mesh_);
auto prop2 = OpenMesh::VProp<int>(0, mesh_, "ids");
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = prop1;
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "copying property temporary to named took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Temporary property got destroyed";
prop1.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
auto prop3 = OpenMesh::VProp<int>(mesh_, "ids");
EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], -13) << "property with name 'ids' was not correctly changed";
}
// named to unnamed
{
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids2");
auto prop2 = OpenMesh::VProp<int>(mesh_);
prop2.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = prop1;
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "copying property named to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
prop1.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
}
// named to named (different names)
{
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids3");
auto prop2 = OpenMesh::VProp<int>(mesh_, "ids4");
prop2.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = prop1;
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "copying property named to named with different name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
prop1.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
}
// named to named (same names)
{
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids5");
auto prop2 = OpenMesh::VProp<int>(mesh_, "ids5");
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = prop1; // this should be a no op
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "copying property named to named with same name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
prop1.set_range(mesh_.vertices(), 42);
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
auto prop3 = OpenMesh::VProp<int>(mesh_, "ids5");
EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
}
{
auto prop1 = OpenMesh::MProp<int>(mesh_);
*prop1 = 43;
auto prop2 = prop1;
prop2 = prop1;
prop2 = std::move(prop1);
}
}
TEST_F(OpenMeshPropertyManager, property_moving_same_mesh) {
for (int i = 0; i < 1000000; ++i)
mesh_.add_vertex(Mesh::Point());
// unnamed to unnamed
{
auto prop1 = OpenMesh::VProp<int>(mesh_);
auto prop2 = OpenMesh::VProp<int>(mesh_);
prop2.set_range(mesh_.vertices(), 0);
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = std::move(prop1); // this should be cheap
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "moving property temporary to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_FALSE(prop1.isValid()) << "prop1 not invalidated after moving";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
}
// unnamed to named
{
auto prop1 = OpenMesh::VProp<int>(mesh_);
auto prop2 = OpenMesh::VProp<int>(mesh_, "ids");
prop2.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = std::move(prop1);
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "moving property temporary to named took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_FALSE(prop1.isValid()) << "prop1 not invalidated after moving";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
auto prop3 = OpenMesh::VProp<int>(mesh_, "ids");
EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], -13) << "property with name 'ids' was not correctly changed";
}
// named to unnamed
{
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids2");
auto prop2 = OpenMesh::VProp<int>(mesh_);
prop2.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = std::move(prop1); // moving named properties will not invalidate the property and will copy the data
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "moving property named to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
prop1.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
}
// named to named (different names)
{
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids3");
auto prop2 = OpenMesh::VProp<int>(mesh_, "ids4");
prop2.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = std::move(prop1); // moving named properties will not invalidate the property and will copy the data
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "moving property named to named with different name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
prop1.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
}
// named to named (same names)
{
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids5");
auto prop2 = OpenMesh::VProp<int>(mesh_, "ids5");
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = std::move(prop1); // this should be a no op
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "moving property named to named with same name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
prop1.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 0) << "Property not copied correctly";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not copied correctly";
auto prop3 = OpenMesh::VProp<int>(mesh_, "ids5");
EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], 0) << "Property not copied correctly";
}
}
TEST_F(OpenMeshPropertyManager, property_copying_different_mesh) {
for (int i = 0; i < 1000000; ++i)
mesh_.add_vertex(Mesh::Point());
auto copy = mesh_;
for (int i = 0; i < 10; ++i)
copy.add_vertex(Mesh::Point());
// unnamed to unnamed
{
auto prop1 = OpenMesh::VProp<int>(3, mesh_);
auto prop2 = OpenMesh::VProp<int>(0, copy);
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 3) << "Property not initialized correctly";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = prop1;
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "copying property temporary to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Temporary property got destroyed";
prop1.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 0) << "Property not copied correctly";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
EXPECT_NO_FATAL_FAILURE(prop2[OpenMesh::VertexHandle(static_cast<int>(copy.n_vertices())-1)]) << "Property not correctly resized";
}
// unnamed to named
{
auto prop1 = OpenMesh::VProp<int>(mesh_);
auto prop2 = OpenMesh::VProp<int>(0, copy, "ids");
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = prop1;
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "copying property temporary to named took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Temporary property got destroyed";
prop1.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
auto prop3 = OpenMesh::VProp<int>(copy, "ids");
EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], -13) << "property with name 'ids' was not correctly changed";
}
// named to unnamed
{
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids2");
auto prop2 = OpenMesh::VProp<int>(copy);
prop2.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = prop1;
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "copying property named to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
prop1.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
}
// named to named (different names)
{
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids3");
auto prop2 = OpenMesh::VProp<int>(copy, "ids4");
prop2.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = prop1;
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "copying property named to named with different name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
prop1.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
}
// named to named (same names)
{
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids5");
auto prop2 = OpenMesh::VProp<int>(copy, "ids5");
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = prop1; // this should be a no op
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "copying property named to named with same name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
prop1.set_range(mesh_.vertices(), 42);
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
auto prop3 = OpenMesh::VProp<int>(mesh_, "ids5");
EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
auto prop4 = OpenMesh::VProp<int>(copy, "ids5");
EXPECT_EQ(prop4[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
}
}
TEST_F(OpenMeshPropertyManager, property_moving_different_mesh) {
for (int i = 0; i < 1000000; ++i)
mesh_.add_vertex(Mesh::Point());
auto copy = mesh_;
for (int i = 0; i < 10; ++i)
copy.add_vertex(Mesh::Point());
// unnamed to unnamed
{
auto prop1 = OpenMesh::VProp<int>(mesh_);
auto prop2 = OpenMesh::VProp<int>(copy);
prop2.set_range(mesh_.vertices(), 0);
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = std::move(prop1); // this should be cheap
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "moving property temporary to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_FALSE(prop1.isValid()) << "prop1 not invalidated after moving";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
EXPECT_NO_FATAL_FAILURE(prop2[OpenMesh::VertexHandle(static_cast<int>(copy.n_vertices())-1)]) << "Property not correctly resized";
}
// unnamed to named
{
auto prop1 = OpenMesh::VProp<int>(mesh_);
auto prop2 = OpenMesh::VProp<int>(copy, "ids");
prop2.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = std::move(prop1);
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "moving property temporary to named took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_FALSE(prop1.isValid()) << "prop1 not invalidated after moving";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
auto prop3 = OpenMesh::VProp<int>(copy, "ids");
EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], -13) << "property with name 'ids' was not correctly changed";
}
// named to unnamed
{
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids2");
auto prop2 = OpenMesh::VProp<int>(copy);
prop2.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = std::move(prop1); // moving named properties will not invalidate the property and will copy the data
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "moving property named to temporary took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
prop1.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
}
// named to named (different names)
{
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids3");
auto prop2 = OpenMesh::VProp<int>(copy, "ids4");
prop2.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], 0) << "Property not initialized correctly";
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = std::move(prop1); // moving named properties will not invalidate the property and will copy the data
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "moving property named to named with different name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
prop1.set_range(mesh_.vertices(), 0);
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
}
// named to named (same names)
{
auto prop1 = OpenMesh::VProp<int>(mesh_, "ids5");
auto prop2 = OpenMesh::VProp<int>(copy, "ids5");
auto prop6 = OpenMesh::Prop<OpenMesh::VertexHandle, int>(mesh_);
prop6 = prop1;
for (auto vh : mesh_.vertices())
prop1[vh] = vh.idx()*2-13;
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
auto t_start = std::chrono::high_resolution_clock::now();
prop2 = std::move(prop1); // should copy
auto t_end = std::chrono::high_resolution_clock::now();
std::cout << "moving property named to named with same name took " << std::chrono::duration_cast<std::chrono::milliseconds>(t_end-t_start).count() << "ms" << std::endl;
EXPECT_TRUE(prop1.isValid()) << "named prop1 should not be invalidated by moving";
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
prop1.set_range(mesh_.vertices(), 42);
EXPECT_EQ(prop1[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
EXPECT_EQ(prop2[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
auto prop3 = OpenMesh::VProp<int>(mesh_, "ids5");
EXPECT_EQ(prop3[OpenMesh::VertexHandle(0)], 42) << "Property not copied correctly";
auto prop4 = OpenMesh::VProp<int>(copy, "ids5");
EXPECT_EQ(prop4[OpenMesh::VertexHandle(0)], -13) << "Property not copied correctly";
}
}
}