Merge branch 'CustomProperties' into 'master'

Automatic loading of properties stored in files without having to manually create the property before reading.

See merge request OpenMesh/OpenMesh!302
This commit is contained in:
Jan Möbius
2021-03-17 09:39:52 +00:00
25 changed files with 1213 additions and 276 deletions

View File

@@ -44,7 +44,10 @@ else ()
vci_add_library (OpenMeshCore SHAREDANDSTATIC ${sources} ${headers})
set_target_properties (OpenMeshCore PROPERTIES VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR} )
endif ()
if (MSVC)
target_compile_options(OpenMeshCore PUBLIC /bigobj)
endif ()
# Add core as dependency before fixbundle

View File

@@ -278,7 +278,6 @@ namespace OMFormat {
}; // Chunk
// ------------------------------------------------------------ Helper
// -------------------- get size information
/// Return size of header in bytes.

View File

@@ -91,31 +91,42 @@ namespace IO {
/// It's used by the OM reader/writer modules.
///
/// The following specialization are provided:
/// - Fundamental types except \c long \c double
/// - Fundamental types except \c long
/// - %OpenMesh vector types
/// - %OpenMesh::StatusInfo
/// - std::string (max. length 65535)
/// - std::vector<T> (requires a specialization for T)
///
/// \todo Complete documentation of members
template < typename T > struct binary
template < typename T, typename = void > struct binary
{
typedef T value_type;
/// Can we store T? Set this to true in your specialization.
static const bool is_streamable = false;
/// What's the size of T? If it depends on the actual value (e.g. for vectors) return UnknownSize
static size_t size_of(void) { return UnknownSize; }
/// What't the size of a specific value of type T.
static size_t size_of(const value_type&) { return UnknownSize; }
/// A string that identifies the type of T.
static std::string type_identifier (void) { return "UnknownType"; }
/// Store a value of T and return the number of bytes written
static
size_t store( std::ostream& /* _os */,
const value_type& /* _v */,
bool /* _swap=false */)
const value_type& /* _v */,
bool /* _swap */ = false ,
bool /* store_size */ = true ) // for vectors
{ X; return 0; }
/// Restore a value of T and return the number of bytes read
static
size_t restore( std::istream& /* _is */,
value_type& /* _v */,
bool /* _swap=false */)
value_type& /* _v */,
bool /* _swap */ = false ,
bool /* store_size */ = true ) // for vectors
{ X; return 0; }
};

View File

@@ -72,6 +72,11 @@
#include <OpenMesh/Core/IO/SR_rbo.hh>
#include <OpenMesh/Core/IO/SR_binary.hh>
#include <OpenMesh/Core/Utils/typename.hh>
#include <iostream>
//== NAMESPACES ===============================================================
namespace OpenMesh {
@@ -91,6 +96,7 @@ namespace IO {
static const bool is_streamable = true; \
static size_t size_of(const value_type&) { return sizeof(value_type); } \
static size_t size_of(void) { return sizeof(value_type); } \
static std::string type_identifier(void) { return #T; } \
static size_t store( std::ostream& _os, const value_type& _val, \
bool _swap=false) { \
value_type tmp = _val; \
@@ -120,12 +126,19 @@ SIMPLE_BINARY(char);
SIMPLE_BINARY(int8_t);
SIMPLE_BINARY(int16_t);
SIMPLE_BINARY(int32_t);
SIMPLE_BINARY(int64_t);
//SIMPLE_BINARY(int64_t); // TODO: This does not work. Find out why.
SIMPLE_BINARY(uint8_t);
SIMPLE_BINARY(uint16_t);
SIMPLE_BINARY(uint32_t);
SIMPLE_BINARY(uint64_t);
//handles
SIMPLE_BINARY(FaceHandle);
SIMPLE_BINARY(EdgeHandle);
SIMPLE_BINARY(HalfedgeHandle);
SIMPLE_BINARY(VertexHandle);
SIMPLE_BINARY(MeshHandle);
#undef SIMPLE_BINARY
// For unsigned long which is of size 64 bit on 64 bit
@@ -142,6 +155,7 @@ SIMPLE_BINARY(uint64_t);
static const bool is_streamable = true; \
static size_t size_of(const value_type&) { return sizeof(value_type); } \
static size_t size_of(void) { return sizeof(value_type); } \
static std::string type_identifier(void) { return #T; } \
static size_t store( std::ostream& _os, const value_type& _val, \
bool _swap=false) { \
value_type tmp = _val; \
@@ -172,12 +186,14 @@ SIMPLE_BINARY(unsigned long);
static const bool is_streamable = true; \
static size_t size_of(void) { return sizeof(value_type); } \
static size_t size_of(const value_type&) { return size_of(); } \
static std::string type_identifier(void) { return #T; } \
static size_t store( std::ostream& _os, const value_type& _val, \
bool _swap=false) { \
bool _swap=false) { \
value_type tmp = _val; \
size_t i, b = size_of(_val), N = value_type::size_; \
if (_swap) for (i=0; i<N; ++i) \
reverse_byte_order( tmp[i] ); \
if (_swap) \
for (i=0; i<N; ++i) \
reverse_byte_order( tmp[i] ); \
_os.write( (const char*)&tmp[0], b ); \
return _os.good() ? b : 0; \
} \
@@ -207,6 +223,7 @@ VECTORTS_BINARY( 1 )
VECTORTS_BINARY( 2 )
VECTORTS_BINARY( 3 )
VECTORTS_BINARY( 4 )
VECTORTS_BINARY( 5 )
VECTORTS_BINARY( 6 )
#undef VECTORTS_BINARY
@@ -221,7 +238,7 @@ template <> struct binary< std::string > {
static size_t size_of() { return UnknownSize; }
static size_t size_of(const value_type &_v)
{ return sizeof(length_t) + _v.size(); }
static std::string type_identifier(void) { return "std::string"; }
static
size_t store(std::ostream& _os, const value_type& _v, bool _swap=false)
{
@@ -252,7 +269,6 @@ template <> struct binary< std::string > {
}
};
template <> struct binary<OpenMesh::Attributes::StatusInfo>
{
typedef OpenMesh::Attributes::StatusInfo value_type;
@@ -263,6 +279,7 @@ template <> struct binary<OpenMesh::Attributes::StatusInfo>
static size_t size_of() { return sizeof(status_t); }
static size_t size_of(const value_type&) { return size_of(); }
static std::string type_identifier(void) { return "StatusInfo";}
static size_t n_bytes(size_t _n_elem)
{ return _n_elem*sizeof(status_t); }
@@ -307,8 +324,106 @@ struct FunctorRestore {
bool swap_;
};
#include <OpenMesh/Core/IO/SR_binary_vector_of_fundamentals.inl>
#include <OpenMesh/Core/IO/SR_binary_vector_of_string.inl>
template <typename T>
struct binary< std::vector< T >, typename std::enable_if<std::is_default_constructible<T>::value>::type > {
typedef std::vector< T > value_type;
typedef typename value_type::value_type elem_type;
static const bool is_streamable = binary<T>::is_streamable;
static size_t size_of(bool _store_size = true)
{ return IO::UnknownSize; }
static size_t size_of(const value_type& _v, bool _store_size = true)
{
if(binary<T>::size_of() != IO::UnknownSize)
{
unsigned int N = _v.size();
auto res = binary<T>::size_of()*_v.size() + (_store_size? sizeof(decltype(N)) : 0);
return res;
}
else
{
size_t size = 0;
for(auto v : _v)
size += binary<T>::size_of(v);
if(_store_size)
size += binary<unsigned int>::size_of();
return size;
}
}
static std::string type_identifier(void) { return "std::vector<" + binary<T>::type_identifier() + ">"; }
static
size_t store(std::ostream& _os, const value_type& _v, bool _swap=false, bool _store_size = true) {
size_t bytes=0;
if(_store_size)
{
unsigned int N = _v.size();
bytes += binary<unsigned int>::store( _os, N, _swap );
}
if (_swap)
bytes += std::accumulate( _v.begin(), _v.end(), static_cast<size_t>(0),
FunctorStore<elem_type>(_os,_swap) );
else
{
auto elem_size = binary<elem_type>::size_of();
if (elem_size != IO::UnknownSize && elem_size == sizeof(elem_type))
{
// size of all elements is known, equal, and densely packed in vector.
// Just store vector data
auto bytes_of_vec = size_of(_v, false);
bytes += bytes_of_vec;
if (_v.size() > 0)
_os.write( reinterpret_cast<const char*>(&_v[0]), bytes_of_vec);
}
else
{
// store individual elements
for (const auto& v : _v)
bytes += binary<elem_type>::store(_os, v, _swap);
}
}
return _os.good() ? bytes : 0;
}
static size_t restore(std::istream& _is, value_type& _v, bool _swap=false, bool _restore_size = true) {
size_t bytes=0;
if(_restore_size)
{
unsigned int size_of_vec;
bytes += binary<unsigned int>::restore(_is, size_of_vec, _swap);
_v.resize(size_of_vec);
}
if ( _swap)
bytes += std::accumulate( _v.begin(), _v.end(), size_t(0),
FunctorRestore<elem_type>(_is, _swap) );
else
{
auto elem_size = binary<elem_type>::size_of();
if (elem_size != IO::UnknownSize && elem_size == sizeof(elem_type))
{
// size of all elements is known, equal, and densely packed in vector.
// Just restore vector data
auto bytes_of_vec = size_of(_v, false);
bytes += bytes_of_vec;
if (_v.size() > 0)
_is.read( reinterpret_cast<char*>(&_v[0]), bytes_of_vec );
}
else
{
// restore individual elements
for (auto& v : _v)
bytes += binary<elem_type>::restore(_is, v, _swap);
}
}
return _is.good() ? bytes : 0;
}
};
#include <OpenMesh/Core/IO/SR_binary_vector_of_bool.inl>
// ----------------------------------------------------------------------------

View File

@@ -1,95 +1,103 @@
template <> struct binary< std::vector<bool> >
{
typedef std::vector< bool > value_type;
typedef value_type::value_type elem_type;
static const bool is_streamable = true;
static size_t size_of(void) { return UnknownSize; }
static size_t size_of(const value_type& _v)
static size_t size_of(bool _store_size = true) { return UnknownSize; }
static size_t size_of(const value_type& _v, bool _store_size = true)
{
return _v.size() / 8 + ((_v.size() % 8)!=0);
size_t size = _v.size() / 8 + ((_v.size() % 8)!=0);
if(_store_size)
size += binary<unsigned int>::size_of();
return size;
}
static std::string type_identifier(void) { return "std::vector<bool>"; }
static
size_t store( std::ostream& _ostr, const value_type& _v, bool )
size_t store( std::ostream& _ostr, const value_type& _v, bool _swap, bool _store_size = true)
{
size_t bytes = 0;
size_t N = _v.size() / 8;
size_t R = _v.size() % 8;
size_t idx; // element index
if(_store_size)
{
unsigned int size_N = _v.size();
bytes += binary<unsigned int>::store( _ostr, size_N, _swap );
}
size_t idx; // element index
size_t bidx;
unsigned char bits; // bitset
for (idx=0; idx < N; ++idx)
for (bidx=idx=0; idx < N; ++idx, bidx+=8)
{
bits = static_cast<unsigned char>(_v[idx])
| (static_cast<unsigned char>(_v[idx+1]) << 1)
| (static_cast<unsigned char>(_v[idx+2]) << 2)
| (static_cast<unsigned char>(_v[idx+3]) << 3)
| (static_cast<unsigned char>(_v[idx+4]) << 4)
| (static_cast<unsigned char>(_v[idx+5]) << 5)
| (static_cast<unsigned char>(_v[idx+6]) << 6)
| (static_cast<unsigned char>(_v[idx+7]) << 7);
bits = static_cast<unsigned char>(_v[bidx])
| (static_cast<unsigned char>(_v[bidx+1]) << 1)
| (static_cast<unsigned char>(_v[bidx+2]) << 2)
| (static_cast<unsigned char>(_v[bidx+3]) << 3)
| (static_cast<unsigned char>(_v[bidx+4]) << 4)
| (static_cast<unsigned char>(_v[bidx+5]) << 5)
| (static_cast<unsigned char>(_v[bidx+6]) << 6)
| (static_cast<unsigned char>(_v[bidx+7]) << 7);
_ostr << bits;
}
bytes = N;
bytes += N;
if (R)
{
bits = 0;
switch(R)
{
case 7: bits |= (static_cast<unsigned char>(_v[idx+6]) << 6);
case 6: bits |= (static_cast<unsigned char>(_v[idx+5]) << 5);
case 5: bits |= (static_cast<unsigned char>(_v[idx+4]) << 4);
case 4: bits |= (static_cast<unsigned char>(_v[idx+3]) << 3);
case 3: bits |= (static_cast<unsigned char>(_v[idx+2]) << 2);
case 2: bits |= (static_cast<unsigned char>(_v[idx+1]) << 1);
case 1: bits |= static_cast<unsigned char>(_v[idx+0]);
}
for (idx=0; idx < R; ++idx)
bits |= static_cast<unsigned char>(_v[bidx+idx]) << idx;
_ostr << bits;
++bytes;
}
assert( bytes == size_of(_v) );
assert( bytes == size_of(_v, _store_size) );
return bytes;
}
static
size_t restore( std::istream& _istr, value_type& _v, bool )
size_t restore( std::istream& _istr, value_type& _v, bool _swap, bool _restore_size = true)
{
size_t bytes = 0;
if(_restore_size)
{
unsigned int size_of_vec;
bytes += binary<unsigned int>::restore(_istr, size_of_vec, _swap);
_v.resize(size_of_vec);
}
size_t N = _v.size() / 8;
size_t R = _v.size() % 8;
size_t idx; // element index
size_t bidx; //
unsigned char bits; // bitset
for (idx=0; idx < N; ++idx)
for (bidx=idx=0; idx < N; ++idx, bidx+=8)
{
_istr >> bits;
_v[idx+0] = ((bits & 0x01)!=0);
_v[idx+1] = ((bits & 0x02)!=0);
_v[idx+2] = ((bits & 0x04)!=0);
_v[idx+3] = ((bits & 0x08)!=0);
_v[idx+4] = ((bits & 0x10)!=0);
_v[idx+5] = ((bits & 0x20)!=0);
_v[idx+6] = ((bits & 0x40)!=0);
_v[idx+7] = ((bits & 0x80)!=0);
_v[bidx+0] = (bits & 0x01) != 0;
_v[bidx+1] = (bits & 0x02) != 0;
_v[bidx+2] = (bits & 0x04) != 0;
_v[bidx+3] = (bits & 0x08) != 0;
_v[bidx+4] = (bits & 0x10) != 0;
_v[bidx+5] = (bits & 0x20) != 0;
_v[bidx+6] = (bits & 0x40) != 0;
_v[bidx+7] = (bits & 0x80) != 0;
}
bytes = N;
bytes += N;
if (R)
{
_istr >> bits;
for(; idx < _v.size(); ++idx)
_v[idx] = (bits & (1 << (idx%8)))!=0;
for (idx=0; idx < R; ++idx)
_v[bidx+idx] = (bits & (1<<idx)) != 0;
++bytes;
}

View File

@@ -1,53 +0,0 @@
#define BINARY_VECTOR( T ) \
template <> struct binary< std::vector< T > > { \
typedef std::vector< T > value_type; \
typedef value_type::value_type elem_type; \
\
static const bool is_streamable = true; \
\
static size_t size_of(void) \
{ return IO::UnknownSize; } \
\
static size_t size_of(const value_type& _v) \
{ return sizeof(elem_type)*_v.size(); } \
\
static \
size_t store(std::ostream& _os, const value_type& _v, bool _swap=false) { \
size_t bytes=0; \
\
if (_swap) \
bytes = std::accumulate( _v.begin(), _v.end(), bytes, \
FunctorStore<elem_type>(_os,_swap) ); \
else { \
bytes = size_of(_v); \
_os.write( reinterpret_cast<const char*>(&_v[0]), bytes ); \
} \
return _os.good() ? bytes : 0; \
} \
\
static size_t restore(std::istream& _is, value_type& _v, bool _swap=false) { \
size_t bytes=0; \
\
if ( _swap) \
bytes = std::accumulate( _v.begin(), _v.end(), size_t(0), \
FunctorRestore<elem_type>(_is, _swap) ); \
else \
{ \
bytes = size_of(_v); \
_is.read( reinterpret_cast<char*>(&_v[0]), bytes ); \
} \
return _is.good() ? bytes : 0; \
} \
}
BINARY_VECTOR( short );
BINARY_VECTOR( unsigned short );
BINARY_VECTOR( int );
BINARY_VECTOR( unsigned int );
BINARY_VECTOR( long );
BINARY_VECTOR( unsigned long );
BINARY_VECTOR( float );
BINARY_VECTOR( double );
#undef BINARY_VECTOR

View File

@@ -1,39 +0,0 @@
template <> struct binary< std::vector< std::string > >
{
// struct binary interface
typedef std::vector< std::string > value_type;
typedef value_type::value_type elem_type;
static const bool is_streamable = true;
// Helper
struct Sum
{
size_t operator() ( size_t _v1, const elem_type& _s2 )
{ return _v1 + binary<elem_type>::size_of(_s2); }
};
// struct binary interface
static size_t size_of(void) { return UnknownSize; }
static size_t size_of(const value_type& _v)
{ return std::accumulate( _v.begin(), _v.end(), size_t(0), Sum() ); }
static
size_t store(std::ostream& _os, const value_type& _v, bool _swap=false)
{
return std::accumulate( _v.begin(), _v.end(), size_t(0),
FunctorStore<elem_type>(_os, _swap) );
}
static
size_t restore(std::istream& _is, value_type& _v, bool _swap=false)
{
return std::accumulate( _v.begin(), _v.end(), size_t(0),
FunctorRestore<elem_type>(_is, _swap) );
}
};

View File

@@ -89,18 +89,34 @@ template <typename T> inline
size_t size_of( const T& _v )
{ return binary< T >::size_of(_v); }
template <typename T> inline
size_t size_of( const std::vector<T> & _v, bool _store_size = true)
{ return binary< std::vector<T> >::size_of(_v, _store_size); }
template <typename T> inline
size_t size_of(void)
{ return binary< T >::size_of(); }
template <typename T> inline
size_t store( std::ostream& _os, const T& _v, bool _swap=false)
size_t size_of(bool _store_size)
{ return binary< std::vector<T> >::size_of(_store_size); }
template <typename T> inline
size_t store( std::ostream& _os, const T& _v, bool _swap =false)
{ return binary< T >::store( _os, _v, _swap ); }
template <typename T> inline
size_t restore( std::istream& _is, T& _v, bool _swap=false)
size_t store( std::ostream& _os, const std::vector<T>& _v, bool _swap=false, bool _store_size = true)
{ return binary< std::vector<T> >::store( _os, _v, _swap, _store_size); }
template <typename T> inline
size_t restore( std::istream& _is, T& _v, bool _swap = false)
{ return binary< T >::restore( _is, _v, _swap ); }
template <typename T> inline
size_t restore( std::istream& _is, std::vector<T>& _v, bool _swap=false, bool _restore_size = true)
{ return binary< std::vector<T> >::restore( _is, _v, _swap, _restore_size); }
//@}

View File

@@ -152,6 +152,9 @@ public:
// set incident face handle for given halfedge
virtual void set_face(HalfedgeHandle _heh, FaceHandle _fh) = 0;
// request texture coordinate property
virtual void request_face_texcoords2D() = 0;
// set vertex texture coordinate
virtual void set_texcoord(HalfedgeHandle _heh, const Vec2f& _texcoord) = 0;

View File

@@ -285,9 +285,14 @@ public:
mesh_.set_face_handle(_heh, _fh);
}
virtual void request_face_texcoords2D() override
{
if(!mesh_.has_halfedge_texcoords2D())
mesh_.request_halfedge_texcoords2D();
}
virtual void set_texcoord(HalfedgeHandle _heh, const Vec2f& _texcoord) override
{
{
if (mesh_.has_halfedge_texcoords2D())
mesh_.set_texcoord2D(_heh, vector_cast<TexCoord2D>(_texcoord));
}

View File

@@ -57,7 +57,9 @@
#include <OpenMesh/Core/IO/OMFormat.hh>
#include <OpenMesh/Core/IO/reader/OMReader.hh>
#include <OpenMesh/Core/IO/writer/OMWriter.hh>
#include <OpenMesh/Core/Utils/typename.hh>
#include <OpenMesh/Core/Utils/PropertyCreator.hh>
//=== NAMESPACES ==============================================================
@@ -180,7 +182,6 @@ bool _OMReader_::read_binary(std::istream& _is, BaseImporter& _bi, Options& _opt
return false;
}
while (!_is.eof()) {
bytes_ += restore(_is, chunk_header_, swap);
@@ -400,10 +401,17 @@ bool _OMReader_::read_binary_vertex_chunk(std::istream &_is, BaseImporter &_bi,
}
case Chunk::Type_Custom:
{
if(header_.version_ > OMFormat::mk_version(2,1))
{
Chunk::PropertyName property_type;
bytes_ += restore(_is, property_type, _swap);
add_generic_property(property_type, _bi);
}
bytes_ += restore_binary_custom_data(_is, _bi.kernel()->_get_vprop(property_name_), header_.n_vertices_, _swap);
vidx = header_.n_vertices_;
}
break;
@@ -550,12 +558,20 @@ bool _OMReader_::read_binary_face_chunk(std::istream &_is, BaseImporter &_bi, Op
}
case Chunk::Type_Custom:
{
if(header_.version_ > OMFormat::mk_version(2,1))
{
Chunk::PropertyName property_type;
bytes_ += restore(_is, property_type, _swap);
add_generic_property(property_type, _bi);
}
bytes_ += restore_binary_custom_data(_is, _bi.kernel()->_get_fprop(property_name_), header_.n_faces_, _swap);
fidx = header_.n_faces_;
break;
}
default: // skip unknown chunks
{
@@ -583,11 +599,18 @@ bool _OMReader_::read_binary_edge_chunk(std::istream &_is, BaseImporter &_bi, Op
switch (chunk_header_.type_) {
case Chunk::Type_Custom:
{
if(header_.version_ > OMFormat::mk_version(2,1))
{
Chunk::PropertyName property_type;
bytes_ += restore(_is, property_type, _swap);
add_generic_property(property_type, _bi);
}
bytes_ += restore_binary_custom_data(_is, _bi.kernel()->_get_eprop(property_name_), header_.n_edges_, _swap);
break;
}
case Chunk::Type_Status:
{
assert( OMFormat::dimensions(chunk_header_) == 1);
@@ -626,10 +649,39 @@ bool _OMReader_::read_binary_halfedge_chunk(std::istream &_is, BaseImporter &_bi
switch (chunk_header_.type_) {
case Chunk::Type_Custom:
{
if(header_.version_ > OMFormat::mk_version(2,1))
{
Chunk::PropertyName property_type;
bytes_ += restore(_is, property_type, _swap);
add_generic_property(property_type, _bi);
}
bytes_ += restore_binary_custom_data(_is, _bi.kernel()->_get_hprop(property_name_), 2 * header_.n_edges_, _swap);
break;
}
//----------------------------------------------------------------------------------------
case Chunk::Type_Texcoord:
{
assert( OMFormat::dimensions(chunk_header_) == size_t(OpenMesh::Vec2f::dim()));
//fileOptions_ += OpenMesh::IO::Options::FaceTexCoord;
if (_opt.face_has_texcoord())
{
_bi.request_face_texcoords2D();
}
OpenMesh::Vec2f v2f;
for (size_t e_idx = 0; e_idx < header_.n_edges_*2; ++e_idx)
{
bytes_ += vector_restore(_is, v2f, _swap);
if (_opt.face_has_texcoord())
_bi.set_texcoord(HalfedgeHandle(int(e_idx)), v2f);
}
break;
}
//----------------------------------------------------------------------------------------
case Chunk::Type_Topology:
{
std::vector<HalfedgeHandle> next_halfedges;
@@ -703,11 +755,18 @@ bool _OMReader_::read_binary_mesh_chunk(std::istream &_is, BaseImporter &_bi, Op
switch (chunk_header_.type_) {
case Chunk::Type_Custom:
{
if(header_.version_ > OMFormat::mk_version(2,1))
{
Chunk::PropertyName property_type;
bytes_ += restore(_is, property_type, _swap);
add_generic_property(property_type, _bi);
}
bytes_ += restore_binary_custom_data(_is, _bi.kernel()->_get_mprop(property_name_), 1, _swap);
break;
}
default:
// skip unknown chunk
size_t chunk_size = OMFormat::chunk_data_size(header_, chunk_header_);
@@ -747,14 +806,14 @@ size_t _OMReader_::restore_binary_custom_data(std::istream& _is, BaseProperty* _
#endif
#if defined(OM_DEBUG)
assert( block_size == b );
assert( block_size == b);
#endif
assert( block_size == _bp->size_of());
block_size = 0;
} else {
omerr() << "Warning! Property " << _bp->name() << " not loaded: " << "Mismatching data sizes!n";
omerr() << "Warning! Property " << _bp->name() << " not loaded: " << "Mismatching data sizes!" << std::endl;
}
}
@@ -767,6 +826,57 @@ size_t _OMReader_::restore_binary_custom_data(std::istream& _is, BaseProperty* _
}
//--------------------------------helper
void _OMReader_:: add_generic_property(OMFormat::Chunk::PropertyName& _property_type, BaseImporter& _bi) const
{
// We want to support the old way of restoring properties, ie.
// the user has manually created the corresponding property.
// In that case the property does not need be registered.
// However, the _bi.kerne()->_get_prop(property_name_) checks below
// May return a property with the correct name but the wrong type.
// For now we will have to live with that.
PropertyCreationManager& manager = PropertyCreationManager::instance();
switch (chunk_header_.entity_)
{
case OMFormat::Chunk::Entity_Vertex:
{
if (_bi.kernel()->_get_vprop(property_name_) == nullptr)
manager.create_property<OpenMesh::VertexHandle>(*_bi.kernel(), _property_type, property_name_);
break;
}
case OMFormat::Chunk::Entity_Face:
{
if (_bi.kernel()->_get_fprop(property_name_) == nullptr)
manager.create_property<OpenMesh::FaceHandle>(*_bi.kernel(), _property_type, property_name_);
break;
}
case OMFormat::Chunk::Entity_Edge:
{
if (_bi.kernel()->_get_eprop(property_name_) == nullptr)
manager.create_property<OpenMesh::EdgeHandle>(*_bi.kernel(), _property_type, property_name_);
break;
}
case OMFormat::Chunk::Entity_Halfedge:
{
if (_bi.kernel()->_get_hprop(property_name_) == nullptr)
manager.create_property<OpenMesh::HalfedgeHandle>(*_bi.kernel(), _property_type, property_name_);
break;
}
case OMFormat::Chunk::Entity_Mesh:
{
if (_bi.kernel()->_get_mprop(property_name_) == nullptr)
manager.create_property<OpenMesh::MeshHandle>(*_bi.kernel(), _property_type, property_name_);
break;
}
case OMFormat::Chunk::Entity_Sentinel:
;
break;
default:
;
}
}
//-----------------------------------------------------------------------------
//=============================================================================

View File

@@ -149,11 +149,14 @@ private:
Options &_opt,
bool _swap) const;
size_t restore_binary_custom_data( std::istream& _is,
BaseProperty* _bp,
size_t _n_elem,
bool _swap) const;
size_t restore_binary_custom_data(std::istream& _is,
BaseProperty* _bp,
size_t _n_elem,
bool _swap) const;
//------------------helper
private:
void add_generic_property(OMFormat::Chunk::PropertyName& _property_type, BaseImporter& _bi) const;
};

View File

@@ -81,7 +81,7 @@ _OMWriter_& OMWriter() { return __OMWriterInstance; }
const OMFormat::uchar _OMWriter_::magic_[3] = "OM";
const OMFormat::uint8 _OMWriter_::version_ = OMFormat::mk_version(2,1);
const OMFormat::uint8 _OMWriter_::version_ = OMFormat::mk_version(2,2);
_OMWriter_::
@@ -303,7 +303,6 @@ bool _OMWriter_::write_binary(std::ostream& _os, BaseExporter& _be,
chunk_header.dim_ = OMFormat::dim(t);
chunk_header.bits_ = OMFormat::bits(t[0]);
// std::clog << chunk_header << std::endl;
bytes += store(_os, chunk_header, swap);
for (i = 0, nV = header.n_vertices_; i < nV; ++i)
@@ -337,6 +336,30 @@ bool _OMWriter_::write_binary(std::ostream& _os, BaseExporter& _be,
}
}
// ---------- write face texture coords
if (_OMWriter_::version_ > OMFormat::mk_version(2,1) && _be.n_edges() && _opt.check(Options::FaceTexCoord))
{
t = _be.texcoord(HalfedgeHandle(0));
chunk_header.name_ = false;
chunk_header.entity_ = OMFormat::Chunk::Entity_Halfedge;
chunk_header.type_ = OMFormat::Chunk::Type_Texcoord;
chunk_header.signed_ = OMFormat::is_signed(t[0]);
chunk_header.float_ = OMFormat::is_float(t[0]);
chunk_header.dim_ = OMFormat::dim(t);
chunk_header.bits_ = OMFormat::bits(t[0]);
bytes += store(_os, chunk_header, swap);
unsigned int nHE;
for (i = 0, nHE = header.n_edges_*2; i < nHE; ++i)
bytes += vector_store(_os, _be.texcoord(HalfedgeHandle(i)), swap);
}
//---------------------------------------------------------------
// ---------- write vertex topology (outgoing halfedge)
if (_be.n_vertices())
{
@@ -573,7 +596,7 @@ bool _OMWriter_::write_binary(std::ostream& _os, BaseExporter& _be,
chunk_header.entity_ = OMFormat::Chunk::Entity_Sentinel;
bytes += store(_os, chunk_header, swap);
// std::clog << "#bytes written: " << bytes << std::endl;
omlog() << "#bytes written: " << bytes << std::endl;
return true;
}
@@ -581,7 +604,7 @@ bool _OMWriter_::write_binary(std::ostream& _os, BaseExporter& _be,
// ----------------------------------------------------------------------------
size_t _OMWriter_::store_binary_custom_chunk(std::ostream& _os,
const BaseProperty& _bp,
BaseProperty& _bp,
OMFormat::Chunk::Entity _entity,
bool _swap) const
{
@@ -620,16 +643,22 @@ size_t _OMWriter_::store_binary_custom_chunk(std::ostream& _os,
// 2. property name
bytes += store( _os, OMFormat::Chunk::PropertyName(_bp.name()), _swap );
// 3. block size
bytes += store( _os, _bp.size_of(), OMFormat::Chunk::Integer_32, _swap );
//omlog() << " n_bytes = " << _bp.size_of() << std::endl;
// 3. data type needed to add property automatically, supported by version 2.1 or later
if(_OMWriter_::version_ > OMFormat::mk_version(2,1))
{
OMFormat::Chunk::PropertyName type = OMFormat::Chunk::PropertyName(_bp.get_storage_name());
bytes += store(_os, type, _swap);
}
// 4. data
// 4. block size
bytes += store( _os, _bp.size_of(), OMFormat::Chunk::Integer_32, _swap );
//omlog() << " block size = " << _bp.size_of() << std::endl;
// 5. data
{
size_t b;
bytes += ( b=_bp.store( _os, _swap ) );
//omlog() << " b = " << b << std::endl;
assert( b == _bp.size_of() );
assert(b == _bp.size_of());
}
return bytes;
}

View File

@@ -121,8 +121,8 @@ protected:
bool write_binary(std::ostream&, BaseExporter&, Options) const;
size_t store_binary_custom_chunk( std::ostream&, const BaseProperty&,
OMFormat::Chunk::Entity, bool) const;
size_t store_binary_custom_chunk(std::ostream&, BaseProperty&,
OMFormat::Chunk::Entity, bool) const;
};

View File

@@ -133,6 +133,9 @@ public: // I/O support
/// persistency.
virtual void set_persistent( bool _yn ) = 0;
///returns a unique string for the type of the elements
virtual std::string get_storage_name() const = 0;
/// Number of elements in property
virtual size_t n_elements() const = 0;

View File

@@ -41,9 +41,9 @@
#ifndef OPENMESH_PROPERTY_HH
#define OPENMESH_PROPERTY_HH
//#ifndef OPENMESH_PROPERTY_HH
//#define OPENMESH_PROPERTY_HH
#pragma once
//== INCLUDES =================================================================
@@ -55,6 +55,9 @@
#include <string>
#include <algorithm>
#include <OpenMesh/Core/IO/SR_store.hh>
#include <iostream>
//== NAMESPACES ===============================================================
@@ -147,21 +150,23 @@ public:
virtual size_t store( std::ostream& _ostr, bool _swap ) const override
{
if ( IO::is_streamable<vector_type>() )
return IO::store(_ostr, data_, _swap );
if (IO::is_streamable<vector_type>() && element_size() != IO::UnknownSize)
return IO::store(_ostr, data_, _swap, false); //does not need to store its length
size_t bytes = 0;
for (size_t i=0; i<n_elements(); ++i)
bytes += IO::store( _ostr, data_[i], _swap );
bytes += IO::store( _ostr, data_[i], _swap);
return bytes;
}
virtual size_t restore( std::istream& _istr, bool _swap ) override
{
if ( IO::is_streamable<vector_type>() )
return IO::restore(_istr, data_, _swap );
if ( IO::is_streamable<vector_type>() && element_size() != IO::UnknownSize)
return IO::restore(_istr, data_, _swap, false); //does not need to restore its length
size_t bytes = 0;
for (size_t i=0; i<n_elements(); ++i)
bytes += IO::restore( _istr, data_[i], _swap );
bytes += IO::restore( _istr, data_[i], _swap);
return bytes;
}
@@ -207,6 +212,10 @@ public: // data access interface
return p;
}
std::string get_storage_name() const override
{
return OpenMesh::IO::binary<T>::type_identifier();
}
private:
@@ -371,6 +380,11 @@ public:
return p;
}
std::string get_storage_name() const override
{
return OpenMesh::IO::binary<bool>::type_identifier();
}
private:
@@ -381,88 +395,6 @@ private:
//-----------------------------------------------------------------------------
/** Property specialization for std::string type.
*/
template <>
class PropertyT<std::string> : public BaseProperty
{
public:
typedef std::string Value;
typedef std::vector<std::string> vector_type;
typedef std::string value_type;
typedef vector_type::reference reference;
typedef vector_type::const_reference const_reference;
public:
explicit PropertyT(const std::string& _name = "<unknown>", const std::string& _internal_type_name="" )
: BaseProperty(_name, _internal_type_name)
{ }
public: // inherited from BaseProperty
virtual void reserve(size_t _n) override { data_.reserve(_n); }
virtual void resize(size_t _n) override { data_.resize(_n); }
virtual void clear() override { data_.clear(); vector_type().swap(data_); }
virtual void push_back() override { data_.push_back(std::string()); }
virtual void swap(size_t _i0, size_t _i1) override {
std::swap(data_[_i0], data_[_i1]);
}
virtual void copy(size_t _i0, size_t _i1) override
{ data_[_i1] = data_[_i0]; }
public:
virtual void set_persistent( bool _yn ) override
{ check_and_set_persistent<std::string>( _yn ); }
virtual size_t n_elements() const override { return data_.size(); }
virtual size_t element_size() const override { return UnknownSize; }
virtual size_t size_of() const override
{ return IO::size_of( data_ ); }
virtual size_t size_of(size_t /* _n_elem */) const override
{ return UnknownSize; }
/// Store self as one binary block. Max. length of a string is 65535 bytes.
size_t store( std::ostream& _ostr, bool _swap ) const override
{ return IO::store( _ostr, data_, _swap ); }
size_t restore( std::istream& _istr, bool _swap ) override
{ return IO::restore( _istr, data_, _swap ); }
public:
const value_type* data() const {
if( data_.empty() )
return nullptr;
return (value_type*) &data_[0];
}
/// Access the i'th element. No range check is performed!
reference operator[](int _idx) {
assert( size_t(_idx) < data_.size());
return ((value_type*) &data_[0])[_idx];
}
/// Const access the i'th element. No range check is performed!
const_reference operator[](int _idx) const {
assert( size_t(_idx) < data_.size());
return ((value_type*) &data_[0])[_idx];
}
PropertyT<value_type>* clone() const override {
PropertyT<value_type>* p = new PropertyT<value_type>( *this );
return p;
}
private:
vector_type data_;
};
/// Base property handle.
template <class T>
struct BasePropHandleT : public BaseHandle
@@ -578,7 +510,13 @@ struct PropHandle<FaceHandle> {
using type = FPropHandleT<T>;
};
template <>
struct PropHandle<MeshHandle> {
template <typename T>
using type = MPropHandleT<T>;
};
} // namespace OpenMesh
//=============================================================================
#endif // OPENMESH_PROPERTY_HH defined
//#endif // OPENMESH_PROPERTY_HH defined
//=============================================================================

View File

@@ -0,0 +1,153 @@
/* ========================================================================= *
* *
* OpenMesh *
* Copyright (c) 2001-2020, 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. *
* *
* ========================================================================= */
#include "PropertyCreator.hh"
#include <OpenMesh/Core/IO/importer/BaseImporter.hh>
#include <OpenMesh/Core/Mesh/Handles.hh>
#include <OpenMesh/Core/Geometry/VectorT.hh>
#include <OpenMesh/Core/IO/SR_types.hh>
namespace OpenMesh {
PropertyCreationManager& PropertyCreationManager::instance()
{
static PropertyCreationManager manager;
return manager;
}
bool PropertyCreator::can_you_create(const std::string& _type_name)
{
return _type_name == type_string();
}
} /* namespace OpenMesh */
OM_REGISTER_PROPERTY_TYPE(FaceHandle)
OM_REGISTER_PROPERTY_TYPE(EdgeHandle)
OM_REGISTER_PROPERTY_TYPE(HalfedgeHandle)
OM_REGISTER_PROPERTY_TYPE(VertexHandle)
OM_REGISTER_PROPERTY_TYPE(MeshHandle)
OM_REGISTER_PROPERTY_TYPE(bool)
OM_REGISTER_PROPERTY_TYPE(float)
OM_REGISTER_PROPERTY_TYPE(double)
OM_REGISTER_PROPERTY_TYPE(long double)
OM_REGISTER_PROPERTY_TYPE(char)
OM_REGISTER_PROPERTY_TYPE(OpenMesh::IO::int8_t )
OM_REGISTER_PROPERTY_TYPE(OpenMesh::IO::int16_t )
OM_REGISTER_PROPERTY_TYPE(OpenMesh::IO::int32_t )
//OM_REGISTER_PROPERTY_TYPE(OpenMesh::IO::int64_t )
OM_REGISTER_PROPERTY_TYPE(OpenMesh::IO::uint8_t )
OM_REGISTER_PROPERTY_TYPE(OpenMesh::IO::uint16_t)
OM_REGISTER_PROPERTY_TYPE(OpenMesh::IO::uint32_t)
OM_REGISTER_PROPERTY_TYPE(OpenMesh::IO::uint64_t)
OM_REGISTER_PROPERTY_TYPE(std::string)
OM_REGISTER_PROPERTY_TYPE(std::vector<bool>)
OM_REGISTER_PROPERTY_TYPE(std::vector<float>)
OM_REGISTER_PROPERTY_TYPE(std::vector<double>)
OM_REGISTER_PROPERTY_TYPE(std::vector<long double>)
OM_REGISTER_PROPERTY_TYPE(std::vector<char>)
OM_REGISTER_PROPERTY_TYPE(std::vector<OpenMesh::IO::int8_t >)
OM_REGISTER_PROPERTY_TYPE(std::vector<OpenMesh::IO::int16_t >)
OM_REGISTER_PROPERTY_TYPE(std::vector<OpenMesh::IO::int32_t >)
OM_REGISTER_PROPERTY_TYPE(std::vector<OpenMesh::IO::uint8_t >)
OM_REGISTER_PROPERTY_TYPE(std::vector<OpenMesh::IO::uint16_t>)
OM_REGISTER_PROPERTY_TYPE(std::vector<OpenMesh::IO::uint32_t>)
OM_REGISTER_PROPERTY_TYPE(std::vector<OpenMesh::IO::uint64_t>)
OM_REGISTER_PROPERTY_TYPE(std::vector<std::string>)
OM_REGISTER_PROPERTY_TYPE(Vec1c);
OM_REGISTER_PROPERTY_TYPE(Vec1uc);
OM_REGISTER_PROPERTY_TYPE(Vec1s);
OM_REGISTER_PROPERTY_TYPE(Vec1us);
OM_REGISTER_PROPERTY_TYPE(Vec1i);
OM_REGISTER_PROPERTY_TYPE(Vec1ui);
OM_REGISTER_PROPERTY_TYPE(Vec1f);
OM_REGISTER_PROPERTY_TYPE(Vec1d);
OM_REGISTER_PROPERTY_TYPE(Vec2c);
OM_REGISTER_PROPERTY_TYPE(Vec2uc);
OM_REGISTER_PROPERTY_TYPE(Vec2s);
OM_REGISTER_PROPERTY_TYPE(Vec2us);
OM_REGISTER_PROPERTY_TYPE(Vec2i);
OM_REGISTER_PROPERTY_TYPE(Vec2ui);
OM_REGISTER_PROPERTY_TYPE(Vec2f);
OM_REGISTER_PROPERTY_TYPE(Vec2d);
OM_REGISTER_PROPERTY_TYPE(Vec3c);
OM_REGISTER_PROPERTY_TYPE(Vec3uc);
OM_REGISTER_PROPERTY_TYPE(Vec3s);
OM_REGISTER_PROPERTY_TYPE(Vec3us);
OM_REGISTER_PROPERTY_TYPE(Vec3i);
OM_REGISTER_PROPERTY_TYPE(Vec3ui);
OM_REGISTER_PROPERTY_TYPE(Vec3f);
OM_REGISTER_PROPERTY_TYPE(Vec3d);
OM_REGISTER_PROPERTY_TYPE(Vec4c);
OM_REGISTER_PROPERTY_TYPE(Vec4uc);
OM_REGISTER_PROPERTY_TYPE(Vec4s);
OM_REGISTER_PROPERTY_TYPE(Vec4us);
OM_REGISTER_PROPERTY_TYPE(Vec4i);
OM_REGISTER_PROPERTY_TYPE(Vec4ui);
OM_REGISTER_PROPERTY_TYPE(Vec4f);
OM_REGISTER_PROPERTY_TYPE(Vec4d);
OM_REGISTER_PROPERTY_TYPE(Vec5c);
OM_REGISTER_PROPERTY_TYPE(Vec5uc);
OM_REGISTER_PROPERTY_TYPE(Vec5s);
OM_REGISTER_PROPERTY_TYPE(Vec5us);
OM_REGISTER_PROPERTY_TYPE(Vec5i);
OM_REGISTER_PROPERTY_TYPE(Vec5ui);
OM_REGISTER_PROPERTY_TYPE(Vec5f);
OM_REGISTER_PROPERTY_TYPE(Vec5d);
OM_REGISTER_PROPERTY_TYPE(Vec6c);
OM_REGISTER_PROPERTY_TYPE(Vec6uc);
OM_REGISTER_PROPERTY_TYPE(Vec6s);
OM_REGISTER_PROPERTY_TYPE(Vec6us);
OM_REGISTER_PROPERTY_TYPE(Vec6i);
OM_REGISTER_PROPERTY_TYPE(Vec6ui);
OM_REGISTER_PROPERTY_TYPE(Vec6f);
OM_REGISTER_PROPERTY_TYPE(Vec6d);

View File

@@ -0,0 +1,242 @@
/* ========================================================================= *
* *
* OpenMesh *
* Copyright (c) 2001-2020, 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. *
* *
* ========================================================================= */
#pragma once
#include <OpenMesh/Core/System/config.h>
#include <OpenMesh/Core/Utils/HandleToPropHandle.hh>
#include <OpenMesh/Core/Utils/PropertyManager.hh>
#include <OpenMesh/Core/Mesh/PolyConnectivity.hh>
#include <sstream>
#include <stdexcept>
#include <string>
#include <memory>
#include <OpenMesh/Core/IO/SR_store.hh>
namespace OpenMesh {
#define OM_CONCAT_IMPL(a, b) a##b
#define OM_CONCAT(a, b) OM_CONCAT_IMPL(a, b)
/** \brief Base class for property creators
*
* A PropertyCreator can add a named property to a mesh.
* The type of the property is specified in the classes derived from this class.
*
* */
class OPENMESHDLLEXPORT PropertyCreator
{
public:
/// The string that corresponds to the type this property creator can create
virtual std::string type_string() = 0;
virtual std::string type_id_string() = 0;
/// Returns true iff the given _type_name corresponds to the string the derived class can create a property for
bool can_you_create(const std::string &_type_name);
/// Create a vertex property on _mesh with name _property_name
virtual void create_vertex_property (BaseKernel& _mesh, const std::string& _property_name) = 0;
/// Create a halfedge property on _mesh with name _property_name
virtual void create_halfedge_property(BaseKernel& _mesh, const std::string& _property_name) = 0;
/// Create an edge property on _mesh with name _property_name
virtual void create_edge_property (BaseKernel& _mesh, const std::string& _property_name) = 0;
/// Create a face property on _mesh with name _property_name
virtual void create_face_property (BaseKernel& _mesh, const std::string& _property_name) = 0;
/// Create a mesh property on _mesh with name _property_name
virtual void create_mesh_property (BaseKernel& _mesh, const std::string& _property_name) = 0;
/// Create a property for the element of type HandleT on _mesh with name _property_name
template <typename HandleT>
void create_property(BaseKernel& _mesh, const std::string& _property_name);
virtual ~PropertyCreator() {}
protected:
PropertyCreator() {}
};
template <> inline void PropertyCreator::create_property<VertexHandle> (BaseKernel& _mesh, const std::string& _property_name) { create_vertex_property (_mesh, _property_name); }
template <> inline void PropertyCreator::create_property<HalfedgeHandle>(BaseKernel& _mesh, const std::string& _property_name) { create_halfedge_property(_mesh, _property_name); }
template <> inline void PropertyCreator::create_property<EdgeHandle> (BaseKernel& _mesh, const std::string& _property_name) { create_edge_property (_mesh, _property_name); }
template <> inline void PropertyCreator::create_property<FaceHandle> (BaseKernel& _mesh, const std::string& _property_name) { create_face_property (_mesh, _property_name); }
template <> inline void PropertyCreator::create_property<MeshHandle> (BaseKernel& _mesh, const std::string& _property_name) { create_mesh_property (_mesh, _property_name); }
/// Helper class that contains the implementation of the create_<HandleT>_property methods.
/// Implementation is injected into PropertyCreatorT.
template <typename PropertyCreatorT>
class PropertyCreatorImpl : public PropertyCreator
{
public:
std::string type_id_string() override { return get_type_name<typename PropertyCreatorT::type>(); }
template <typename HandleT, typename PropT>
void create_prop(BaseKernel& _mesh, const std::string& _property_name)
{
using PHandle = typename PropHandle<HandleT>::template type<PropT>;
PHandle prop;
if (!_mesh.get_property_handle(prop, _property_name))
_mesh.add_property(prop, _property_name);
}
void create_vertex_property (BaseKernel& _mesh, const std::string& _property_name) override { create_prop<VertexHandle , typename PropertyCreatorT::type>(_mesh, _property_name); }
void create_halfedge_property(BaseKernel& _mesh, const std::string& _property_name) override { create_prop<HalfedgeHandle, typename PropertyCreatorT::type>(_mesh, _property_name);}
void create_edge_property (BaseKernel& _mesh, const std::string& _property_name) override { create_prop<EdgeHandle , typename PropertyCreatorT::type>(_mesh, _property_name);}
void create_face_property (BaseKernel& _mesh, const std::string& _property_name) override { create_prop<FaceHandle , typename PropertyCreatorT::type>(_mesh, _property_name);}
void create_mesh_property (BaseKernel& _mesh, const std::string& _property_name) override { create_prop<MeshHandle , typename PropertyCreatorT::type>(_mesh, _property_name);}
~PropertyCreatorImpl() override {}
protected:
PropertyCreatorImpl() {}
};
/// Actual PropertyCreators specialize this class in order to add properties of type T
namespace {
template <typename T>
class PropertyCreatorT : public PropertyCreatorImpl<PropertyCreatorT<T>>
{
};
}
///used to register custom type, where typename.hh does provide a string for type recognition
#define OM_REGISTER_PROPERTY_TYPE(ClassName) \
namespace OpenMesh { \
namespace { /* ensure internal linkage of class */ \
template <> \
class PropertyCreatorT<ClassName> : public PropertyCreatorImpl<PropertyCreatorT<ClassName>> \
{ \
public: \
using type = ClassName; \
std::string type_string() override { return OpenMesh::IO::binary<type>::type_identifier(); } \
\
PropertyCreatorT() \
{ \
PropertyCreationManager::instance().register_property_creator(this); \
} \
~PropertyCreatorT() override {} \
}; \
} \
/* static to ensure internal linkage of object */ \
static PropertyCreatorT<ClassName> OM_CONCAT(property_creator_registration_object_, __LINE__); \
}
/** \brief Class for adding properties based on strings
*
* The PropertyCreationManager holds all PropertyCreators and dispatches the property creation
* to them if they are able to create a property for a given string.
*
* If you want to be able to store your custom properties into a file and automatically load
* them without manually adding the property yourself you can register your type by calling the
* OM_REGISTER_PROPERTY_TYPE(ClassName, TypeString)
* */
class OPENMESHDLLEXPORT PropertyCreationManager
{
public:
static PropertyCreationManager& instance();
template <typename HandleT>
void create_property(BaseKernel& _mesh, const std::string& _type_name, const std::string& _property_name)
{
auto can_create = [_type_name](OpenMesh::PropertyCreator* pc){
return pc->can_you_create(_type_name);
};
std::vector<OpenMesh::PropertyCreator*>::iterator pc_iter = std::find_if(property_creators_.begin(),
property_creators_.end(), can_create);
if (pc_iter != property_creators_.end())
{
const auto& pc = *pc_iter;
pc->create_property<HandleT>(_mesh, _property_name);
return;
}
omerr() << "No property creator registered that can create a property of type " << _type_name << std::endl;
omerr() << "You need to register your custom type using OM_REGISTER_PROPERTY_TYPE(ClassName) and declare the struct binary<ClassName>.\
See documentation for more details." << std::endl;
omerr() << "Adding property failed." << std::endl;
}
void register_property_creator(PropertyCreator* _property_creator)
{
for (auto pc : property_creators_)
if (pc->type_string() == _property_creator->type_string())
{
if (pc->type_id_string() != _property_creator->type_id_string())
{
omerr() << "And it looks like you are trying to add a different type with an already existing string identification." << std::endl;
omerr() << "Type id of existing type is " << pc->type_id_string() << " trying to add for " << _property_creator->type_id_string() << std::endl;
}
return;
}
property_creators_.push_back(_property_creator);
}
private:
PropertyCreationManager() {}
~PropertyCreationManager() {}
std::vector<PropertyCreator*> property_creators_;
};
/** \brief Create a property with type corresponding to _type_name on _mesh with name _property_name
*
* For user defined types you need to register the type using OM_REGISTER_PROPERTY_TYPE(ClassName, TypeString)
* */
template <typename HandleT>
void create_property_from_string(BaseKernel& _mesh, const std::string& _type_name, const std::string& _property_name)
{
PropertyCreationManager::instance().create_property<HandleT>(_mesh, _type_name, _property_name);
}
} /* namespace OpenMesh */

View File

@@ -207,7 +207,7 @@ class PropertyManager {
*
* @param mesh The mesh on which to create the property.
*/
PropertyManager(const PolyConnectivity& mesh) : mesh_(mesh), retain_(false), name_("") {
explicit PropertyManager(const PolyConnectivity& mesh) : mesh_(mesh), retain_(false), name_("") {
PropertyManager::mesh().add_property(prop_, name_);
}
@@ -630,7 +630,7 @@ public:
using value_type = typename PropertyT::value_type;
using Handle = typename PropertyT::Handle;
ConstPropertyViewer(const PolyConnectivity& mesh, PropertyT property_handle)
ConstPropertyViewer(const PolyConnectivity& mesh, const PropertyT& property_handle)
:
mesh_(mesh),
prop_(property_handle)

View File

@@ -7,6 +7,9 @@
#include <string>
#include <typeinfo>
#include <vector>
#include <OpenMesh/Core/Mesh/Handles.hh>
#include <OpenMesh/Core/Geometry/VectorT.hh>
namespace OpenMesh {
@@ -23,4 +26,4 @@ std::string get_type_name()
#endif
}
}
}//namespace OpenMesh

View File

@@ -878,4 +878,24 @@ TEST_F(OpenMeshPropertyManager, mesh_property_initialization) {
TEST_F(OpenMeshPropertyManager, property_without_default_constructor ) {
// just check if this compiles
struct NoDefaultConstructor
{
int val;
NoDefaultConstructor() = delete;
NoDefaultConstructor(int i) : val(i) {}
};
OpenMesh::VProp<std::vector<NoDefaultConstructor>> vprop(mesh_);
OpenMesh::HProp<std::vector<NoDefaultConstructor>> hprop(mesh_);
OpenMesh::EProp<std::vector<NoDefaultConstructor>> eprop(mesh_);
OpenMesh::FProp<std::vector<NoDefaultConstructor>> fprop(mesh_);
OpenMesh::MProp<std::vector<NoDefaultConstructor>> mprop(mesh_);
}
}

View File

@@ -1,6 +1,68 @@
#include <gtest/gtest.h>
#include <Unittests/unittests_common.hh>
#include <OpenMesh/Core/Utils/PropertyManager.hh>
#include <OpenMesh/Core/Utils/PropertyCreator.hh>
struct RegisteredDataType{
int ival;
double dval;
bool bval;
OpenMesh::Vec4f vec4fval;
RegisteredDataType() : ival(0), dval(0.0) , bval(false), vec4fval(OpenMesh::Vec4f(0,0,0,0)) {}
RegisteredDataType(int i) : ival(i), dval(i*1.234567), bval(i%2) , vec4fval(OpenMesh::Vec4f(dval,2*dval,3*dval,4*dval)) {}
bool operator==(const RegisteredDataType& _other) const
{
return ival == _other.ival &&
dval == _other.dval &&
bval == _other.bval &&
vec4fval == _other.vec4fval;
}
};
namespace OpenMesh
{
namespace IO
{
template <> struct binary<RegisteredDataType>
{
typedef RegisteredDataType value_type;
static const bool is_streamable = true;
// return binary size of the value
static size_t size_of(void)
{
return sizeof(int)+sizeof(double)+sizeof(bool)+sizeof(OpenMesh::Vec4f);
}
static size_t size_of(const value_type&)
{
return size_of();
}
static std::string type_identifier(void)
{
return "RegisteredDataType";
}
static size_t store(std::ostream& _os, const value_type& _v, bool _swap=false)
{
size_t bytes;
bytes = IO::store( _os, _v.ival, _swap );
bytes += IO::store( _os, _v.dval, _swap );
bytes += IO::store( _os, _v.bval, _swap );
bytes += IO::store( _os, _v.vec4fval, _swap );
return _os.good() ? bytes : 0;
}
static size_t restore( std::istream& _is, value_type& _v, bool _swap=false)
{
size_t bytes;
bytes = IO::restore( _is, _v.ival, _swap );
bytes += IO::restore( _is, _v.dval, _swap );
bytes += IO::restore( _is, _v.bval, _swap );
bytes += IO::restore( _is, _v.vec4fval, _swap );
return _is.good() ? bytes : 0;
}
};
}
}
namespace {
@@ -1458,6 +1520,257 @@ TEST_F(OpenMeshReadWriteOM, LoadPolyMeshVersion_2_0) {
}
std::string get_type_string(OpenMesh::FaceHandle) { return "Face"; }
std::string get_type_string(OpenMesh::EdgeHandle) { return "Edge"; }
std::string get_type_string(OpenMesh::HalfedgeHandle) { return "Halfedge"; }
std::string get_type_string(OpenMesh::VertexHandle) { return "Vertex"; }
std::string get_type_string(char) { return "char"; }
std::string get_type_string(signed char) { return "signed char"; }
std::string get_type_string(double) { return "double"; }
std::string get_type_string(float) { return "float"; }
std::string get_type_string(int) { return "int"; }
std::string get_type_string(short) { return "short"; }
std::string get_type_string(unsigned char) { return "unsigned char"; }
std::string get_type_string(unsigned int) { return "unsigned int"; }
std::string get_type_string(unsigned short) { return "unsigned short"; }
std::string get_type_string(bool) { return "bool"; }
std::string get_type_string(std::string) { return "string"; }
std::string get_type_string(RegisteredDataType) { return "RegisteredDataType"; }
template <typename T>
std::string get_type_string(std::vector<T>) { return "std::vector of " + get_type_string(T()); }
template <typename T, int Dim>
std::string get_type_string(OpenMesh::VectorT<T, Dim>) { return "OM vector of dimension " + std::to_string(Dim) + " of type " + get_type_string(T()); }
template <typename T>
T get_value(int seed, T, int seed2 = 0)
{
return (seed * 3 + seed2) % 20;
}
std::string get_value(int seed, std::string, int seed2 = 0)
{
return std::to_string((seed * 3 + seed2) % 20);
}
template <typename T>
std::vector<T> get_value(int seed, const std::vector<T>&, int _offset = 0)
{
int size = get_value(seed, 3);
std::vector<T> res(size);
for (int i = 0; i < size; ++i)
res[i] = get_value(seed, T(), i + _offset);
return res;
}
template <typename T, int Dim>
OpenMesh::VectorT<T, Dim> get_value(int seed, const OpenMesh::VectorT<T, Dim>&)
{
OpenMesh::VectorT<T, Dim> res;
for (int i = 0; i < Dim; ++i)
res[i] = get_value(seed, T(), i);
return res;
}
template <typename MeshT, typename HandleT, typename T>
OpenMesh::Prop<HandleT, T> add_property(MeshT& _mesh)
{
std::string name = get_type_string(HandleT()) + ": " + get_type_string(T());
OpenMesh::Prop<HandleT, T> prop(_mesh, name.c_str());
_mesh.property(prop.getRawProperty()).set_persistent(true);
for (auto e : _mesh.template elements<HandleT>())
prop[e] = get_value(e.idx(), T());
return prop;
}
template <typename MeshT, typename HandleT, typename T>
void check_property(MeshT& _mesh)
{
std::string name = get_type_string(HandleT()) + ": " + get_type_string(T());
bool has_prop = OpenMesh::hasProperty<HandleT, T>(_mesh, name.c_str());
EXPECT_TRUE(has_prop) << "Property " << name << " is not available";
if (!has_prop)
return;
OpenMesh::Prop<HandleT, T> prop(_mesh, name.c_str());
for (auto e : _mesh.template elements<HandleT>())
EXPECT_EQ(prop[e], get_value(e.idx(), T())) << "For property " << name;
}
template <typename MeshT, typename HandleT, typename T>
void request_property(MeshT& _mesh)
{
std::string name = get_type_string(HandleT()) + ": " + get_type_string(T());
OpenMesh::Prop<HandleT, T> prop(_mesh, name.c_str());
}
enum class PropertyAction
{
Add, Check, Request
};
// For a given Handle and Type add, check or request a property
template <typename MeshT, typename HandleT, typename T>
void do_property(MeshT& _mesh, PropertyAction action)
{
switch (action)
{
case PropertyAction::Add:
add_property<MeshT, HandleT, T>(_mesh);
break;
case PropertyAction::Check:
check_property<MeshT, HandleT, T>(_mesh);
break;
case PropertyAction::Request:
request_property<MeshT, HandleT, T>(_mesh);
break;
}
}
// for a given handle do action for OpenMesh Vector of dimension Dim with many differnt types
template <typename MeshT, typename HandleT, int Dim>
void do_all_property_types_vec(MeshT& _mesh, PropertyAction action)
{
do_property<MeshT, HandleT, OpenMesh::VectorT<signed char , Dim>>(_mesh, action);
do_property<MeshT, HandleT, OpenMesh::VectorT<double , Dim>>(_mesh, action);
do_property<MeshT, HandleT, OpenMesh::VectorT<float , Dim>>(_mesh, action);
do_property<MeshT, HandleT, OpenMesh::VectorT<int , Dim>>(_mesh, action);
do_property<MeshT, HandleT, OpenMesh::VectorT<short , Dim>>(_mesh, action);
do_property<MeshT, HandleT, OpenMesh::VectorT<unsigned char , Dim>>(_mesh, action);
do_property<MeshT, HandleT, OpenMesh::VectorT<unsigned int , Dim>>(_mesh, action);
do_property<MeshT, HandleT, OpenMesh::VectorT<unsigned short, Dim>>(_mesh, action);
}
// for a given handle do action for OpenMesh Vectors of dimensions 1 to 4
template <typename MeshT, typename HandleT>
void do_all_property_types_vec_all_dim(MeshT& _mesh, PropertyAction action)
{
do_all_property_types_vec<MeshT, HandleT, 1>(_mesh, action);
do_all_property_types_vec<MeshT, HandleT, 2>(_mesh, action);
do_all_property_types_vec<MeshT, HandleT, 3>(_mesh, action);
do_all_property_types_vec<MeshT, HandleT, 4>(_mesh, action);
}
// for a given handle type do action for many different types
template <typename MeshT, typename HandleT>
void do_all_property_types(MeshT& _mesh, PropertyAction action, int version)
{
do_property<MeshT, HandleT, int> (_mesh, action);
do_property<MeshT, HandleT, double> (_mesh, action);
do_property<MeshT, HandleT, float> (_mesh, action);
do_property<MeshT, HandleT, char> (_mesh, action);
do_property<MeshT, HandleT, bool> (_mesh, action);
do_property<MeshT, HandleT, std::string> (_mesh, action);
do_property<MeshT, HandleT, RegisteredDataType> (_mesh, action);
if(version >= 22)
{
do_property<MeshT, HandleT, std::vector<int>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<double>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<float>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<char>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<bool>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<std::string>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<RegisteredDataType>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<std::vector<int>>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<std::vector<double>>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<std::vector<float>>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<std::vector<char>>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<std::vector<bool>>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<std::vector<std::string>>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<std::vector<RegisteredDataType>>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<std::vector<std::vector<int>>>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<std::vector<std::vector<double>>>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<std::vector<std::vector<float>>>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<std::vector<std::vector<char>>>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<std::vector<std::vector<bool>>>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<std::vector<std::vector<std::string>>>> (_mesh, action);
do_property<MeshT, HandleT, std::vector<std::vector<std::vector<RegisteredDataType>>>> (_mesh, action);
}
do_all_property_types_vec_all_dim<MeshT, HandleT>(_mesh, action);
}
// Do action for all property types for faces, edges, halfedges and vertices
template <typename MeshT>
void do_all_properties(MeshT& _mesh, PropertyAction action, int version)
{
do_all_property_types<MeshT,OpenMesh::FaceHandle> (_mesh, action, version);
do_all_property_types<MeshT,OpenMesh::EdgeHandle> (_mesh, action, version);
do_all_property_types<MeshT,OpenMesh::HalfedgeHandle>(_mesh, action, version);
do_all_property_types<MeshT,OpenMesh::VertexHandle> (_mesh, action, version);
}
template <typename MeshT> void add_all_properties(MeshT& _mesh, int version) { do_all_properties(_mesh, PropertyAction::Add , version); }
template <typename MeshT> void check_all_properties(MeshT& _mesh, int version) { do_all_properties(_mesh, PropertyAction::Check , version); }
template <typename MeshT> void request_all_properties(MeshT& _mesh, int version) { do_all_properties(_mesh, PropertyAction::Request, version); }
/*
* Load a triangle mesh from an om file of version 2.1 with properties
*/
TEST_F(OpenMeshReadWriteOM, LoadTriangleMeshWithPropertiesVersion_2_1) {
int version = 21;
mesh_.clear();
std::string file_name = "cube_tri_with_properties_2_1.om";
request_all_properties(mesh_, version);
bool ok = OpenMesh::IO::read_mesh(mesh_, file_name);
ASSERT_TRUE(ok) << file_name;
ASSERT_EQ(8u , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
ASSERT_EQ(18u , mesh_.n_edges()) << "The number of loaded edges is not correct!";
ASSERT_EQ(12u , mesh_.n_faces()) << "The number of loaded faces is not correct!";
ASSERT_EQ(36u , mesh_.n_halfedges()) << "The number of loaded halfedges is not correct!";
check_all_properties(mesh_, version);
}
/*
* store and load a triangle mesh from an om file of the current version with properties
*/
TEST_F(OpenMeshReadWriteOM, LoadTriangleMeshWithPropertiesCurrentVersion) {
// TODO: create a LoadTriangleMeshWithPropertiesVersion_2_2 unittest from the file resulting from this test
// so we will keep testing version 2.2 in the future.
int version = 22;
mesh_.clear();
std::string file_name = "cube_tri_version_2_0.om";
bool ok = OpenMesh::IO::read_mesh(mesh_, file_name);
ASSERT_TRUE(ok) << file_name;
ASSERT_EQ(8u , mesh_.n_vertices()) << "The number of loaded vertices is not correct!";
ASSERT_EQ(18u , mesh_.n_edges()) << "The number of loaded edges is not correct!";
ASSERT_EQ(12u , mesh_.n_faces()) << "The number of loaded faces is not correct!";
ASSERT_EQ(36u , mesh_.n_halfedges()) << "The number of loaded halfedges is not correct!";
add_all_properties(mesh_, version);
mesh_.request_halfedge_texcoords2D();
std::string file_name_2_2 = "cube_tri_with_properties_2_2.om";
OpenMesh::IO::Options ops(OpenMesh::IO::Options::Custom);
ops += OpenMesh::IO::Options::FaceTexCoord;
OpenMesh::IO::write_mesh(mesh_, file_name_2_2, ops);
Mesh new_mesh;
OpenMesh::IO::read_mesh(new_mesh, file_name_2_2, ops);
check_all_properties(new_mesh, version);
EXPECT_TRUE(new_mesh.has_halfedge_texcoords2D());
}
/*
* Try to load mesh from om file with a version that is not yet supported
*/
@@ -1509,6 +1822,39 @@ TEST_F(OpenMeshReadWriteOM, WriteAndLoadDoubles) {
EXPECT_EQ(mesh.normal(OpenMesh::FaceHandle(0)), mesh2.normal(OpenMesh::FaceHandle(0)));
}
/*
* Create Property from String
*/
TEST_F(OpenMeshReadWriteOM, PropertyFromString)
{
{
std::string int_prop_name = "my int prop";
OpenMesh::create_property_from_string<OpenMesh::VertexHandle>(mesh_, "int32_t", int_prop_name);
bool has_int_prop = OpenMesh::hasProperty<OpenMesh::VertexHandle, int>(mesh_, int_prop_name.c_str());
EXPECT_TRUE(has_int_prop);
}
{
std::string double_prop_name = "my double prop";
OpenMesh::create_property_from_string<OpenMesh::VertexHandle>(mesh_, "double", double_prop_name);
bool has_double_prop = OpenMesh::hasProperty<OpenMesh::VertexHandle, double>(mesh_, double_prop_name.c_str());
EXPECT_TRUE(has_double_prop);
}
{
std::string vec_float_prop_name = "my vector of floats prop";
OpenMesh::create_property_from_string<OpenMesh::VertexHandle>(mesh_, "std::vector<float>", vec_float_prop_name);
bool has_vec_float_prop = OpenMesh::hasProperty<OpenMesh::VertexHandle, std::vector<float>>(mesh_, vec_float_prop_name.c_str());
EXPECT_TRUE(has_vec_float_prop);
}
{
std::string MyData_prop_name = "my MyData prop";
OpenMesh::create_property_from_string<OpenMesh::VertexHandle>(mesh_, "RegisteredDataType", MyData_prop_name);
bool has_myData_prop = OpenMesh::hasProperty<OpenMesh::VertexHandle, RegisteredDataType>(mesh_, MyData_prop_name.c_str());
EXPECT_TRUE(has_myData_prop);
}
}
/*
@@ -1558,13 +1904,30 @@ TEST_F(OpenMeshReadWriteOM, WriteAndLoadBoolCheckSpaces) {
// Check if the property is still ok
for (unsigned int i = 0; i < 8; ++i)
{
if ( i == 5)
EXPECT_TRUE( mesh.property(prop,mesh2.vertex_handle((i)) ) );
else
EXPECT_FALSE(mesh.property(prop,mesh2.vertex_handle((i))));
if ( i == 5)
EXPECT_TRUE( mesh.property(prop,mesh2.vertex_handle((i)) ) );
else
EXPECT_FALSE(mesh.property(prop,mesh2.vertex_handle((i))));
}
}
}
OM_REGISTER_PROPERTY_TYPE(RegisteredDataType)
OM_REGISTER_PROPERTY_TYPE(std::vector<std::string>)
OM_REGISTER_PROPERTY_TYPE(std::vector<float>)
OM_REGISTER_PROPERTY_TYPE(std::vector<RegisteredDataType>)
OM_REGISTER_PROPERTY_TYPE(std::vector<std::vector<int>>)
OM_REGISTER_PROPERTY_TYPE(std::vector<std::vector<double>>)
OM_REGISTER_PROPERTY_TYPE(std::vector<std::vector<float>>)
OM_REGISTER_PROPERTY_TYPE(std::vector<std::vector<char>>)
OM_REGISTER_PROPERTY_TYPE(std::vector<std::vector<bool>>)
OM_REGISTER_PROPERTY_TYPE(std::vector<std::vector<std::string>>)
OM_REGISTER_PROPERTY_TYPE(std::vector<std::vector<RegisteredDataType>>)
OM_REGISTER_PROPERTY_TYPE(std::vector<std::vector<std::vector<int>>>)
OM_REGISTER_PROPERTY_TYPE(std::vector<std::vector<std::vector<double>>>)
OM_REGISTER_PROPERTY_TYPE(std::vector<std::vector<std::vector<float>>>)
OM_REGISTER_PROPERTY_TYPE(std::vector<std::vector<std::vector<char>>>)
OM_REGISTER_PROPERTY_TYPE(std::vector<std::vector<std::vector<bool>>>)
OM_REGISTER_PROPERTY_TYPE(std::vector<std::vector<std::vector<std::string>>>)
OM_REGISTER_PROPERTY_TYPE(std::vector<std::vector<std::vector<RegisteredDataType>>>)

View File

@@ -79,6 +79,7 @@ namespace OpenMesh {
return size_of();
}
static std::string type_identifier(void) { return "MyData"; }
static size_t store(std::ostream& _os, const value_type& _v, bool _swap=false)
{
size_t bytes;
@@ -125,6 +126,7 @@ namespace OpenMesh {
return bytes;
}
static std::string type_identifier(void) { return "MyMap"; }
static
size_t store(std::ostream& _os, const value_type& _v, bool _swap=false)
{
@@ -787,7 +789,7 @@ TEST_F(OpenMeshTutorials, storing_custom_properties) {
//Read back mesh
ok = OpenMesh::IO::read_mesh( mesh, "persistence-check.om" );
EXPECT_TRUE(ok) << "Cannot read mesh from file 'persistent-check.om'";
EXPECT_TRUE(ok) << "Cannot read mesh from file 'persistence-check.om'";
// check props
EXPECT_TRUE(fill_props(mesh, vprop_float, true)) << "property not filled correctly";