<li>PropertyManager: PropertyManager only gives const access to the underlying mesh.</li>
</ul>
<b>Core</b>
<ul>
<li>Property System: Get rid of the OM_FORCE_STATIC_CAST defines. We use the type ids to check if the cast is valid or not. This will add more type safety. </li>
<li>Default Traits: Added DefaultTraitsDouble as a version of the default traits that uses double precision for positions and normals as well as float for colors. </li>
<li>Default Mesh Types: Added typdefs for a Triangle Mesh and a PolyMesh which use DefaultTraitsDouble and can be used as default mesh type be the user. </li>
<li>Template Programming Convenience: Added n_elements which returns the number of elements corresponding to the handle type given as template argument. Also added elements and all_elements methods returning ranges of the elements corresponding to the handle type given as template argument.</li>
<li>Smart Handles: Most userfacing functions returning handles should now return smart handles instead. Smart handles know their corresponding mesh and give convenient access to mesh navigation methods.
<li>Smart Ranges: OpenMesh ranges now provide a few methods that simplify a few calculations. See documentation for more details.
</ul>
...
...
@@ -27,6 +36,7 @@
<ul>
<li>Change PropertyManager::operator* to access the property value for mesh properties</li>
@@ -11,34 +11,33 @@ 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:
- makeTemporaryProperty() creates a property that is temporary to the current scope.
- getOrMakeProperty() is used for creating and accessing permanent named properties on a mesh.
- getProperty() is used for accessing an existing permanent named property on a mesh.
Custom properties can be conveniently created and attached to meshes by creating an object of type OpenMesh::PropertyManager. A PropertyManager manages the lifetime of the property and provides read / write access to its values.
All three functions take two template arguments:
- First, the type of the mesh element that the property is attached to (i.e. OpenMesh::VertexHandle, OpenMesh::HalfedgeHandle, OpenMesh::EdgeHandle, or OpenMesh::FaceHandle). <em>Mesh properties</em> (i.e. singleton properties that are attached to an entire mesh instead of individual elements) are accessed by passing \c void instead of a handle type.
- Second, the type of the property value that is attached to each element (e.g., \p int, \p double, etc.).
You can use the typedefs VProp, HProp, EProp, FProp, and MProp in order to create a PropertyManager attached to vertices, halfedge, edges, faces and the mesh respectively. Each of these takes as template argument the type of the property value that is attached to each element (e.g., \p int, \p double, etc.).
We differentiate between two kinds of properties. <em>Named</em> and <em>temporary</em> properties. Temporary properties are created by just providing the constructor with a mesh on which the property should be created. These properties will be removed as soon as the PropertyManager goes out of scope. If in addition to the mesh a property name is provided, a named property will be created which will stay alive even after the PropertyManager goes out of scope. If a PropertyManager is given a name of an already existing property, it will provide read and write access to the same property.
Finally, an optional first parameter can be given containing a value that will be used to initialize the property for all elements if the property is freshly created (i.e. always for temporary properties, and only the first time a specific name is used).
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.
Here are a few examples of how to create and access mesh properties:
\code
// Add a temporary mesh property that stores a double value for every vertex
auto temperature = OpenMesh::getOrMakeProperty<OpenMesh::VertexHandle, double>(mesh, "temperature");
auto temperature = OpenMesh::VProp<double>(mesh);
OpenMesh::VertexHandle vh = ...;
temperature[vh] = 1.0;
// The temperature property will be removed from the mesh when the handle reaches the end of the scope.
// Obtain an existing mesh property that stores a 2D vector for every halfedge
// (Beware: the next line might throw if the expected property doesn't exist.)
auto uv = OpenMesh::getProperty<OpenMesh::HalfedgeHandle, OpenMesh::Vec2d>(mesh, "uv");
// Obtain an existing property that stores a 2D vector for every halfedge
// (or create that property if it does not exist already) and initilize it with the Vector(1,1))
auto uv = OpenMesh::HProp<OpenMesh::Vec2d>(mesh, "uv", OpenMesh::Vec2d(1,1));
// Add a permanent mesh property containing a description string
auto desc = OpenMesh::getOrMakeProperty<void, std::string>(mesh, "desc");
// Obtain an existing mesh property (or create that property if it does not exist already)
// containing a description string
auto desc = OpenMesh::MProp<std::string>(mesh, "desc");
*desc = "This is a very nice mesh.";
\endcode
...
...
@@ -50,7 +49,7 @@ In this example, we will store the \c cog value (see previous example) in a vert
To do so, we first add a (temporary) property of the desired element type (OpenMesh::VertexHandle) and value type (\c %MyMesh::Point) to the mesh:
\dontinclude 03-properties/smooth.cc
\skipline makeTemporaryProperty
\skipline VProp
Enough memory is allocated to hold as many values of \c %MyMesh::Point as there are vertices.
All insert and delete operations on the mesh are synchronized with the attached properties.
...
...
@@ -75,30 +74,19 @@ Below is the complete source code:
## Property Lifetime
In the above example, we chose to use makeTemporaryProperty(). This causes the created property to automatically be removed from the mesh as soon as we leave the scope of the associated handle variable \c cog.
In the above example, we chose to use VProp without a name. This causes the created property to automatically be removed from the mesh as soon as we leave the scope of the associated handle variable \c 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:
If, instead, a property is desired to survive its local scope, it should be created with a name. For example:
\code
auto face_area = OpenMesh::makeTemporaryProperty<OpenMesh::FaceHandle, double>(mesh, "face_area");
\endcode
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:
\code
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.
}
auto face_area = OpenMesh::FProp<double>(mesh, "face_area");
\endcode
Using hasProperty(), we can test whether a mesh has a certain property:
At a later time, we can access the same property by using the same name. If we want to make sure, that we access a property that has already been created earler, we can use hasProperty() to test whether a mesh has the desired property:
\code
if (OpenMesh::hasProperty<OpenMesh::EdgeHandle, bool>(mesh, "is_valley")) {
if (OpenMesh::hasProperty<OpenMesh::FaceHandle, double>(mesh, "face_area")) {
// Property exists. Do something with it.
auto valley = OpenMesh::getProperty<OpenMesh::EdgeHandle, bool>(mesh, "is_valley");
auto valley = OpenMesh::FProp<bool>(mesh, "face_area");
}
else {
// Property does not exist. Do something else.
...
...
@@ -109,9 +97,9 @@ Using hasProperty(), we can test whether a mesh has a certain property:
## Low-Level Property API
The functions makeTemporaryProperty(), getOrMakeProperty(), and getProperty() are the convenient high-level interface for creating and accessing mesh properties.
The property managers VProp, HProp, EProp, FProp and MProp 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).
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(), get_property(), remove_property(), and property() functions and several property handle classes (OpenMesh::VPropHandleT, OpenMesh::HPropHandleT, OpenMesh::EPropHandleT, OpenMesh::FPropHandleT, OpenMesh::MPropHandleT).
- How to use Smart Handles and ranges to navigate on the mesh
- How to use Smart Ranges
So far we have used methods such as halfedge_handle(), next_halfedge_handle(), prev_halfedge_handle(), oppopsite_halfedge_handle(), face_handle(), to_vertex_handle(), and some others, to navigate on that mesh. These functions are defined on a mesh and require as input a handle to an element of the mesh, such as VertexHandle or HalfedgeHandle. In the following example we iterate over all vertices of a triangle mesh and for each vertex we create a list of the vertices that lie opposite of the edges in the ring around the vertex:
For a more concise way of navigating OpenMesh provides smart handles, OpenMesh::SmartVertexHandle, OpenMesh::SmartHalfedgeHandle, OpenMesh::SmartEdgeHandle, and OpenMesh::SmartFaceHandle. Smart handles are smart, because they know to which mesh they belong. This allows them to provide functions for navigating the mesh allowing us to write the above code much simpler:
The ranges of OpenMesh that are returned by functions like voh_range() or outgoing_halfedges() all provide a few methods than can simplify some calculations (see OpenMesh::SmartRangeT). One example is the to_vector() method which convertes the range of elements into a vector containing the elements. All of these methods take a functor as argument (sometimes optional) which is called for each element of the range. With this, the above code can also be implemented like this:
\code
// iterate over vertices of the mesh
for (auto vh : mesh.vertices())
{
// create lambda that returns opposite vertex
auto opposite_vertex = [](OpenMesh::SmartHalfedgeHandle heh) { return heh.next().opp().next().to(); };
// create vector containing all opposite vertices
auto opposite_vertices = vh.outgoing_halfedges().to_vector(opposite_vertex);
}
\endcode
---
## Code Example
In this example, we will use bi-laplacian smoothing on a mesh. We store the \c laplace vector which is the vector pointing from a vertex to the center of gravity of its neighboring vertices in a vertex property.
\dontinclude 11-smart_handles/smooth.cc
\skipline laplace
\skipline laplace
To compute the center of gravity, i.e. the average position, we use the avg() method of the range of 1-ring vertices and pass in a PropertyManager acting as functor returning the corresponding point of a vertex.
\skipline points
\until avg(points)
Similarily we compute the update vector as the laplace of the freshly computed laplace vectors by simply exchanging the points property manager with the laplace property manager.
\skipline Iterate
\until bi_laplace
Finally, we apply the update after damping it by a factor of -0.5.