This examples shows:
- 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 (auto vh : mesh.vertices())
{
std::vector<OpenMesh::VertexHandle> opposite_vertices;
for (auto heh : mesh.voh_range(vh))
{
opposite_vertices.push_back(mesh.to_vertex_handle(mesh.next_halfedge_handle(mesh.opposite_halfedge_handle(mesh.next_halfedge_handle(heh)))));
}
}
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:
for (auto vh : mesh.vertices())
{
std::vector<OpenMesh::VertexHandle> opposite_vertices;
for (auto heh : vh.outgoing_halfedges())
{
opposite_vertices.push_back(heh.next().opp().next().to());
}
}
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:
for (auto vh : mesh.vertices())
{
auto opposite_vertices = vh.outgoing_halfedges().to_vector(opposite_vertex);
}
Definition: SmartHandles.hh:170
Code Example
In this example, we will use bi-laplacian smoothing on a mesh. We store the laplace
vector which is the vector pointing from a vertex to the center of gravity of its neighboring vertices in a vertex property.
This class is intended to manage the lifecycle of properties.
Definition: PropertyManager.hh:76
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.
auto points = OpenMesh::getPointsProperty(mesh);
for (int i = 0; i < iterations; ++i) {
for (const auto& vh : mesh.vertices())
laplace(vh) = vh.vertices().avg(points) - points(vh);
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.
for (const auto& vh : mesh.vertices())
bi_laplace(vh) = (vh.vertices().avg(laplace) - laplace(vh));
Finally, we apply the update after damping it by a factor of -0.5.
for (const auto& vh : mesh.vertices())
points(vh) += -0.5 * bi_laplace(vh);
Below is the complete source code:
#include <OpenMesh/Core/IO/MeshIO.hh>
#include <OpenMesh/Core/Mesh/DefaultTriMesh.hh>
#include <OpenMesh/Core/Utils/PropertyManager.hh>
#include <iostream>
#include <vector>
int main(int argc, char** argv)
{
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];
std::cerr << "Error: Cannot read mesh from " << infile << std::endl;
return 1;
}
{
auto points = OpenMesh::getPointsProperty(mesh);
for (int i = 0; i < iterations; ++i) {
for (const auto& vh : mesh.vertices())
laplace(vh) = vh.vertices().avg(points) - points(vh);
for (const auto& vh : mesh.vertices())
bi_laplace(vh) = (vh.vertices().avg(laplace) - laplace(vh));
for (const auto& vh : mesh.vertices())
points(vh) += -0.5 * bi_laplace(vh);
}
}
std::cerr << "Error: Cannot write mesh to " << outfile << std::endl;
return 1;
}
}
bool read_mesh(Mesh &_mesh, const std::string &_filename)
Read a mesh from file _filename.
Definition: MeshIO.hh:95
Triangle mesh based on the ArrayKernel.
Definition: TriMesh_ArrayKernelT.hh:96
Polygonal mesh based on the ArrayKernel.
Definition: PolyMesh_ArrayKernelT.hh:96