Developer Documentation
Using (custom) properties

This examples shows:

  • How to add and remove custom properties
  • How to get and set the value of a custom property

In the last example we computed the barycenter of each vertex' neighborhood and stored it in an array. It would be more convenient and less error-prone if we could store this data in the mesh and let OpenMesh manage the data. It would be even more helpful if we could attach such properties dynamically to the mesh.

Custom properties can be conveniently created and attached to meshes with the following functions:

All three functions take two template arguments:

All three functions return a handle object (of type OpenMesh::PropertyManager) that manages the lifetime of the property and provides read / write access to its values.

In this example, we will store the cog value (see previous example) in a vertex property instead of keeping it in a separate array. To do so, we first add a (temporary) property of the desired element type (OpenMesh::VertexHandle) and value type (MyMesh::Point) to the mesh:

auto cog = OpenMesh::makeTemporaryProperty<OpenMesh::VertexHandle, MyMesh::Point>(mesh);

Enough memory is allocated to hold as many values of MyMesh::Point as there are vertices. All insert and delete operations on the mesh are synchronized with the attached properties.

Once the property is created, we can use it to compute the centers of the neighborhood of each vertex:

for (const auto& vh : mesh.vertices()) {
cog[vh] = {0,0,0};
int valence = 0;
// Iterate over all 1-ring vertices around vh
for (const auto& vvh : mesh.vv_range(vh)) {
cog[vh] += mesh.point(vvh);
++valence;
}
cog[vh] /= valence;
}

Finally, we set the new position for each vertex:

for (const auto& vh : mesh.vertices()) {
mesh.point(vh) = cog[vh];
}


Since we chose to use makeTemporaryProperty(), the created property is automatically removed from the mesh as soon as we leave the scope of the associated handle variable cog.

If, instead, a property is desired to survive its local scope, it should be created with using getOrMakeProperty(). In that case, the property must be given a name that can later be used to retrieve the property. For example:

auto face_area = OpenMesh::makeTemporaryProperty<OpenMesh::FaceHandle, double>(mesh, 'face_area');

At a later time, we can use the getProperty() function to obtain a handle to a property that was previously created by refering to its name:

try {
auto face_area = OpenMesh::getProperty<OpenMesh::FaceHandle, double>(mesh, 'face_area');
// Use the face_area property.
}
catch (const std::runtime_error& e) {
// Property not found. Handle the error here.
}

The functions makeTemporaryProperty(), getOrMakeProperty(), and getProperty() are the convenient high-level interface for creating and accessing mesh properties.

Beneath these convenience functions, there is also a low-level property interface where handle and property lifetime must be managed manually. This interface is accessed through a mesh's add_property(), remove_property(), and property() functions and several property handle classes (OpenMesh::VPropHandleT, OpenMesh::HPropHandleT, OpenMesh::EPropHandleT, OpenMesh::FPropHandleT, OpenMesh::MPropHandleT).


Below is the complete source code:

#include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
#include <OpenMesh/Core/Utils/PropertyManager.hh>
#include <iostream>
#include <vector>
int main(int argc, char** argv)
{
// Read command line options
MyMesh mesh;
if (argc != 4) {
std::cerr << "Usage: " << argv[0] << " #iterations infile outfile" << std::endl;
return 1;
}
const int iterations = argv[1];
const std::string infile = argv[2];
const std::string outfile = argv[3];
// Read mesh file
if (!OpenMesh::IO::read_mesh(mesh, infile)) {
std::cerr << "Error: Cannot read mesh from " << infile << std::endl;
return 1;
}
{
// Add a vertex property storing the computed centers of gravity
auto cog = OpenMesh::makeTemporaryProperty<OpenMesh::VertexHandle, MyMesh::Point>(mesh);
// Smooth the mesh several times
for (int i = 0; i < iterations; ++i) {
// Iterate over all vertices to compute centers of gravity
for (const auto& vh : mesh.vertices()) {
cog[vh] = {0,0,0};
int valence = 0;
// Iterate over all 1-ring vertices around vh
for (const auto& vvh : mesh.vv_range(vh)) {
cog[vh] += mesh.point(vvh);
++valence;
}
cog[vh] /= valence;
}
// Move all vertices to the previously computed positions
for (const auto& vh : mesh.vertices()) {
mesh.point(vh) = cog[vh];
}
}
} // The cog vertex property is removed from the mesh at the end of this scope
// Write mesh file
if (!OpenMesh::IO::read_mesh(mesh, outfile)) {
std::cerr << "Error: Cannot write mesh to " << outfile << std::endl;
return 1;
}
}