diff --git a/Doc/Tutorial/09-persistence/fill_props.hh b/Doc/Tutorial/10-persistence/fill_props.hh similarity index 100% rename from Doc/Tutorial/09-persistence/fill_props.hh rename to Doc/Tutorial/10-persistence/fill_props.hh diff --git a/Doc/Tutorial/09-persistence/generate_cube.hh b/Doc/Tutorial/10-persistence/generate_cube.hh similarity index 100% rename from Doc/Tutorial/09-persistence/generate_cube.hh rename to Doc/Tutorial/10-persistence/generate_cube.hh diff --git a/Doc/Tutorial/09-persistence/int2roman.cc b/Doc/Tutorial/10-persistence/int2roman.cc similarity index 100% rename from Doc/Tutorial/09-persistence/int2roman.cc rename to Doc/Tutorial/10-persistence/int2roman.cc diff --git a/Doc/Tutorial/09-persistence/int2roman.hh b/Doc/Tutorial/10-persistence/int2roman.hh similarity index 100% rename from Doc/Tutorial/09-persistence/int2roman.hh rename to Doc/Tutorial/10-persistence/int2roman.hh diff --git a/Doc/Tutorial/09-persistence/persistence.cc b/Doc/Tutorial/10-persistence/persistence.cc similarity index 100% rename from Doc/Tutorial/09-persistence/persistence.cc rename to Doc/Tutorial/10-persistence/persistence.cc diff --git a/Doc/Tutorial/09-persistence/stats.hh b/Doc/Tutorial/10-persistence/stats.hh similarity index 100% rename from Doc/Tutorial/09-persistence/stats.hh rename to Doc/Tutorial/10-persistence/stats.hh diff --git a/Doc/tutorial_09.docu b/Doc/tutorial_09.docu index 5f83123a471bcaae735318467684b79677c3516b..3e0a6d8a89d534038153b35192a280160efded59 100644 --- a/Doc/tutorial_09.docu +++ b/Doc/tutorial_09.docu @@ -1,186 +1,82 @@ -/** \page tutorial_09 Storing custom properties +/** \page tutorial_09 Using custom properties -The %OpenMesh' proprietary OM format allows to store and restore -custom properties along with the standard properties. For it we have -to use named custom properties like the following one - -\dontinclude 09-persistence/persistence.cc -\skipline VPropHandleT -\skipline mesh.add_property - -Here we registered a float property for the vertices at the mesh with -name "vprop_float". The name of a property, that we want to make -persistent, must follow a few rules - --# max. 256 characters long --# The prefixes \c "v:", \c "h:", \c "e:", \c "f:" and \c "m:" are reserved. - -If we stick to this rules we are fine. Furthermore we have to -consider, that the names are handled case-sensitive. - -To actually make a custom property persistent we have to set the -persistent flag in the property with - -\skipline mesh.property(vprop_float).set_persistent - -Now we can use \c IO::mesh_write() to write the mesh to a file on -disk. The custom properties are added after the standard properties -in the file, with the name and it's binary size. These two pieces of -information are evaluated when reading the file again. To successfully -restore the custom properties, the mesh must have registered named -properties with equal names (case-sensitive compare). Additionally, -when reading the data, the number of bytes read for a property must -match the provided number in the file. If the OM reader did not find a -suitable named property, it will simply skip it. If the number of bytes -do not match, the complete restore will be terminated and \c -IO::read_mesh() will return \c false. And if the data cannot be -restored, because the appropriate restore method is not available the -exception std::logic_error() will be thrown. - -Since we now know the behaviour, we need to know what kind of data can -we store? Without any further effort, simply using named properties -and setting the persistent flag, we can store following types - -- bool, stored as a bitset -- all other fundamental types except long double, (unsigned) long and size_t -- std::string, each up to 65536 characters long -- OpenMesh::Vec[1,2,3,4,6][c,uc,s,us,i,ui,f,d] - -For further reading we call these types basic types. Apparently we -cannot store non-basic types, which are - -- pointers -- structs/classes -- even more complex data structures, like container of containers. - -However there is a way to store custom types ( else we could not store -std::string). Let's start with an more simple custom data. For -instance we have a struct \c MyData like this - -\dontinclude 09-persistence/persistence.cc -\skipline struct MyData -\until vec4fval -\skipline }; - -Here we keep an int, bool, double value and a vector of 4 floats, which -are all basic types. Then we need to specialize the template struct -OpenMesh::IO::binary<> within the namespace \c OpenMesh::IO - -\skipline binary - -Remember not to use long double, (unsigned) long and size_t as basic types -because of inconsistencies between 32/64bit architectures. - -Herein we have to implement the following set of static member -variables and functions: - -\skipline is_streamable -\skipline size_of -\skipline size_of -\skipline store -\skipline restore - -The flag \c is_streamable has to be set to \c true. Else the data -cannot be stored at all. - -
\c size_of methods
- -Since the size of the custom data can be static, which means we know -the size at compile time, or the size of it is dynamic, which means me -the size is known at runtime, we have to provide the two \c size_of() -methods. - -The first declaration is for the static case, while the second for the -dynamic case. Though the static case is more simple, it is not -straight forward. We cannot simply use \c sizeof() to determine the -data size, because it will return the number ob bytes it needs in -memory (possible 32bit alignment). Instead we need the binary size, -hence we have to add up the single elements in the struct. - -\dontinclude 09-persistence/persistence.cc -\skipline return sizeof - -Actually we would need to sum up the single elements of the vector, -but in this case we know for sure the result (4 floats make 16 bytes, -which is 32bit aligned therefore \c sizeof() returns the wanted -size). But keep in mind, that this a potential location for errors, -when writing custom binary support. - -The second declaration is for the dynamic case, where the custom data -contains pointers or references. This static member must properly -count the data, by disolving the pointers/references, if this data has -to be stored as well. In the dynamic stetting the static variant cannot return -the size, therefore it must return \c IO::UnknownSize. - -In this case the dynamic variant simply returns the size by calling the static -variant, as the sizes are identical for both cases. - -
\c store / \c restore
- -For the dynamic case as for the static case, we have to make up a -scheme how we would store the data. One option is to store the length -of the data and then store the data itself. For instance the type \c -std::string is implemented this way. (We store first the length in a -16bit word (=> max. length 65536), then the characters follow. Hence -\c size_of() returns 2 bytes for the length plus the actual length of -the value \c v.) Since \c MyData contains only basic types we can -implement the necessary methods \c store and \c restore, by simply -breaking up the data into the basic types using the pre-defined -store/restore methods for them: - -\skipline static size_t store -\until } -\skipline static size_t restore -\until } - -It's very important, that the store/restore methods count the -written/read bytes correctly and return the value. On error both -functions must return 0. - -A more complex situation is given with the following property - -\dontinclude 09-persistence/persistence.cc -\skipline MyMap -\skipline mprop_map - -In this case the data contains a container, a map from strings to -integer numbers. If we want to store this as well, we need to make up -a scheme how the map will be stored in a sequential layout. First we -store the number of elements in the map. Then, since the map has an -iterator, we simply iterate over all elements and store each pair -(key/value). This procedure is equal for the \c size_of(), \c store(), and \c -restore() methods. For example the \c size_of() methods look like this - -\dontinclude 09-persistence/persistence.cc -\skip binary< MyMap > -\skipline static size_t size_of -\skipline static size_t size_of -\until } -\until } - -The implementation of \c store() and \c restore() follow a similar pattern. - -The given example program does the following steps - --# Create a mesh and generate a cube --# Add a few custom properties --# Fill them with test data --# Make the properties persistent --# Store mesh in a file named 'persistent-check.om' --# Clear the mesh --# Restore mesh --# Check the content on equality with the test data. - -Since the example is a little bit longer than usual the source is in -several files. The main program is in \c persistence.cc, the cube -generator in \c generate_cube.hh, \c stats.hh provides little tools to -display information about the mesh and the properties, the file \c -fill_props.hh providing the test data, and \c int2roman.hh/.cc, which -is used in fill_props.hh. All necessary parts are in \c -persistence.cc, which is displayed in full length below. For the other -files please have a look in the directory \c -OpenMesh/Doc/Tutorial/09-persistence/. - -\include 09-persistence/persistence.cc - -*/ \ No newline at end of file +This small code example shows how to attach andaccess additional properties on a mesh. + +When you want to add an additional properties you have to attach it to a primitive of the +mesh. You can attach to verticies, halfedges, edges, faces or to the mesh itself. Use the +add_property function: + +\code + + // for each vertex an extra double value + OpenMesh::VPropHandleT< double > vprop_double; + mesh.add_property( vprop_double ,"Vertex property name"); + + // for each halfedge an extra int value + OpenMesh::HEPropHandleT< int > heprop_int; + mesh.add_property( heprop_int ,"Halfedge property name"); + + // for each edge an extra float value + OpenMesh::EPropHandleT< float > eprop_float; + mesh.add_property( eprop_float ,"Edge property name"); + + // for each face an extra double value + OpenMesh::FPropHandleT< double > fprop_double; + mesh.add_property( fprop_double ,"Face property name"); + + // for the mesh an extra string + OpenMesh::MPropHandleT< string > mprop_string; + mesh.add_property( mprop_string , "Mesh property name "); + +\endcode + +Accessing to the property is available via the property function. +This function gets the property handle (created above) and a +handle (e.g. to a vertex or a face): + +\code + // Write something to a face property: + mesh->property( , ) = ; + + // E.g. + for (f_it=mesh->faces_begin(); f_it!=mesh->faces_end() ; ++f_it) { + mesh->property( fprop_double , *f_it ) = 0.0; + } +\endcode + +As you can attach properties to the mesh, you might want to add a property in one +function and access it in another, where the handle is not yet available. You can +retrieve the required handle in the following way (Note that the handles are accessed by their name and type): + +\code + // Specify handle type (Double face handle in this case): + OpenMesh::FPropHandleT< double > fprop_double; + + // Try to get handle with the given name. + if ( !mesh_.get_property_handle(fprop_double,"Face property name") ) { + std::cerr << "Unable to retrieve property! " << std::endl; + return + } + + // If we reach this point, we have a valid handle. +\endcode + +The properties can be removed by calling remove_property: + +\code + // Remove the property + mesh->remove_property(fprop_double); +\endcode + + + +A useful function to see all properties on the mesh is: + +\code + // Print all available properties + mesh->property_stats(); +\endcode + +A useful class for handling properties and their lifetime is the OpenMesh::PropertyManager. + +*/ diff --git a/Doc/tutorial_10.docu b/Doc/tutorial_10.docu new file mode 100644 index 0000000000000000000000000000000000000000..81ff26f2123c549c852320d1d5b719522e4d120b --- /dev/null +++ b/Doc/tutorial_10.docu @@ -0,0 +1,186 @@ +/** \page tutorial_10 Storing custom properties + +The %OpenMesh' proprietary OM format allows to store and restore +custom properties along with the standard properties. For it we have +to use named custom properties like the following one + +\dontinclude 10-persistence/persistence.cc +\skipline VPropHandleT +\skipline mesh.add_property + +Here we registered a float property for the vertices at the mesh with +name "vprop_float". The name of a property, that we want to make +persistent, must follow a few rules + +-# max. 256 characters long +-# The prefixes \c "v:", \c "h:", \c "e:", \c "f:" and \c "m:" are reserved. + +If we stick to this rules we are fine. Furthermore we have to +consider, that the names are handled case-sensitive. + +To actually make a custom property persistent we have to set the +persistent flag in the property with + +\skipline mesh.property(vprop_float).set_persistent + +Now we can use \c IO::mesh_write() to write the mesh to a file on +disk. The custom properties are added after the standard properties +in the file, with the name and it's binary size. These two pieces of +information are evaluated when reading the file again. To successfully +restore the custom properties, the mesh must have registered named +properties with equal names (case-sensitive compare). Additionally, +when reading the data, the number of bytes read for a property must +match the provided number in the file. If the OM reader did not find a +suitable named property, it will simply skip it. If the number of bytes +do not match, the complete restore will be terminated and \c +IO::read_mesh() will return \c false. And if the data cannot be +restored, because the appropriate restore method is not available the +exception std::logic_error() will be thrown. + +Since we now know the behaviour, we need to know what kind of data can +we store? Without any further effort, simply using named properties +and setting the persistent flag, we can store following types + +- bool, stored as a bitset +- all other fundamental types except long double, (unsigned) long and size_t +- std::string, each up to 65536 characters long +- OpenMesh::Vec[1,2,3,4,6][c,uc,s,us,i,ui,f,d] + +For further reading we call these types basic types. Apparently we +cannot store non-basic types, which are + +- pointers +- structs/classes +- even more complex data structures, like container of containers. + +However there is a way to store custom types ( else we could not store +std::string). Let's start with an more simple custom data. For +instance we have a struct \c MyData like this + +\dontinclude 10-persistence/persistence.cc +\skipline struct MyData +\until vec4fval +\skipline }; + +Here we keep an int, bool, double value and a vector of 4 floats, which +are all basic types. Then we need to specialize the template struct +OpenMesh::IO::binary<> within the namespace \c OpenMesh::IO + +\skipline binary + +Remember not to use long double, (unsigned) long and size_t as basic types +because of inconsistencies between 32/64bit architectures. + +Herein we have to implement the following set of static member +variables and functions: + +\skipline is_streamable +\skipline size_of +\skipline size_of +\skipline store +\skipline restore + +The flag \c is_streamable has to be set to \c true. Else the data +cannot be stored at all. + +
\c size_of methods
+ +Since the size of the custom data can be static, which means we know +the size at compile time, or the size of it is dynamic, which means me +the size is known at runtime, we have to provide the two \c size_of() +methods. + +The first declaration is for the static case, while the second for the +dynamic case. Though the static case is more simple, it is not +straight forward. We cannot simply use \c sizeof() to determine the +data size, because it will return the number ob bytes it needs in +memory (possible 32bit alignment). Instead we need the binary size, +hence we have to add up the single elements in the struct. + +\dontinclude 10-persistence/persistence.cc +\skipline return sizeof + +Actually we would need to sum up the single elements of the vector, +but in this case we know for sure the result (4 floats make 16 bytes, +which is 32bit aligned therefore \c sizeof() returns the wanted +size). But keep in mind, that this a potential location for errors, +when writing custom binary support. + +The second declaration is for the dynamic case, where the custom data +contains pointers or references. This static member must properly +count the data, by disolving the pointers/references, if this data has +to be stored as well. In the dynamic stetting the static variant cannot return +the size, therefore it must return \c IO::UnknownSize. + +In this case the dynamic variant simply returns the size by calling the static +variant, as the sizes are identical for both cases. + +
\c store / \c restore
+ +For the dynamic case as for the static case, we have to make up a +scheme how we would store the data. One option is to store the length +of the data and then store the data itself. For instance the type \c +std::string is implemented this way. (We store first the length in a +16bit word (=> max. length 65536), then the characters follow. Hence +\c size_of() returns 2 bytes for the length plus the actual length of +the value \c v.) Since \c MyData contains only basic types we can +implement the necessary methods \c store and \c restore, by simply +breaking up the data into the basic types using the pre-defined +store/restore methods for them: + +\skipline static size_t store +\until } +\skipline static size_t restore +\until } + +It's very important, that the store/restore methods count the +written/read bytes correctly and return the value. On error both +functions must return 0. + +A more complex situation is given with the following property + +\dontinclude 10-persistence/persistence.cc +\skipline MyMap +\skipline mprop_map + +In this case the data contains a container, a map from strings to +integer numbers. If we want to store this as well, we need to make up +a scheme how the map will be stored in a sequential layout. First we +store the number of elements in the map. Then, since the map has an +iterator, we simply iterate over all elements and store each pair +(key/value). This procedure is equal for the \c size_of(), \c store(), and \c +restore() methods. For example the \c size_of() methods look like this + +\dontinclude 10-persistence/persistence.cc +\skip binary< MyMap > +\skipline static size_t size_of +\skipline static size_t size_of +\until } +\until } + +The implementation of \c store() and \c restore() follow a similar pattern. + +The given example program does the following steps + +-# Create a mesh and generate a cube +-# Add a few custom properties +-# Fill them with test data +-# Make the properties persistent +-# Store mesh in a file named 'persistent-check.om' +-# Clear the mesh +-# Restore mesh +-# Check the content on equality with the test data. + +Since the example is a little bit longer than usual the source is in +several files. The main program is in \c persistence.cc, the cube +generator in \c generate_cube.hh, \c stats.hh provides little tools to +display information about the mesh and the properties, the file \c +fill_props.hh providing the test data, and \c int2roman.hh/.cc, which +is used in fill_props.hh. All necessary parts are in \c +persistence.cc, which is displayed in full length below. For the other +files please have a look in the directory \c +OpenMesh/Doc/Tutorial/10-persistence/. + +\include 10-persistence/persistence.cc + +*/ diff --git a/Doc/tutorial_main.docu b/Doc/tutorial_main.docu index d07e9ab77e3cf5768631b063aaa1f539ff8be796..13ff26cd28d8776c8a619cc446020cd4268e64bb 100644 --- a/Doc/tutorial_main.docu +++ b/Doc/tutorial_main.docu @@ -39,6 +39,7 @@ repeatedly replacing each vertex' position by the center of gravity
  • \subpage tutorial_07b
  • \subpage tutorial_08
  • \subpage tutorial_09 +
  • \subpage tutorial_10 */