for_each
construct, since it is easier to read and may be more efficient than hand-written loops.We will define a class which provides the smoothing algorithm, hence define a reusable component. The class must be template class because there is no such thing as a class OpenMesh, but many different types of OpenMesh:
template <class Mesh> class SmootherT
The class SmootherT has two functors, one that computes the barycenter for a given vertex, and a second that sets the vertex position to the corresponding barycenter. A functor is simply a class with a function operator()(...)
. The first functor ComputeCOG
computes the barycenter and store it in a custom vertex property cog_
:
void operator()(typename Mesh::Vertex& _v) { typename Mesh::VertexHandle vh( mesh_.handle(_v) ); typename Mesh::VertexVertexIter vv_it; typename Mesh::Scalar valence(0.0); mesh_.property(cog_, vh) = typename Mesh::Point(0.0, 0.0, 0.0); for (vv_it=mesh_.vv_iter(vh); vv_it; ++vv_it) { mesh_.property(cog_, vh) += mesh_.point( vv_it ); ++valence; } mesh_.property(cog_, mesh_.handle(_v) ) /= valence; }
Note, that ComputeCOG
needs to have access to the mesh object and the property handle. Here, both are references to member variables of the smoother object.
The second functor class
SetCOG
, which sets the vertex position, is constructed analogical.
Using these functors and std::for_each
from the STL the smoothing algorithm can be realized in a member function of SmootherT
:
void smooth(unsigned int _iterations) { for (unsigned int i=0; i < _iterations; ++i) { std::for_each(mesh_.vertices_begin(), mesh_.vertices_end(), ComputeCOG(mesh_, cog_)); std::for_each(mesh_.vertices_begin(), mesh_.vertices_end(), SetCOG(mesh_, cog_)); } }
The complete example looks like this:
#include <algorithm> #include <OpenMesh/Core/Utils/Property.hh> #ifndef DOXY_IGNORE_THIS template <class Mesh> class SmootherT { public: typedef typename Mesh::Point cog_t; typedef OpenMesh::VPropHandleT< cog_t > Property_cog; public: // construct with a given mesh SmootherT(Mesh& _mesh) : mesh_(_mesh) { mesh_.add_property( cog_ ); } ~SmootherT() { mesh_.remove_property( cog_ ); } // smooth mesh _iterations times void smooth(unsigned int _iterations) { for (unsigned int i=0; i < _iterations; ++i) { std::for_each(mesh_.vertices_begin(), mesh_.vertices_end(), ComputeCOG(mesh_, cog_)); std::for_each(mesh_.vertices_begin(), mesh_.vertices_end(), SetCOG(mesh_, cog_)); } } private: //--- private classes --- class ComputeCOG { public: ComputeCOG(Mesh& _mesh, Property_cog& _cog) : mesh_(_mesh), cog_(_cog) {} void operator()(typename Mesh::Vertex& _v) { typename Mesh::VertexHandle vh( mesh_.handle(_v) ); typename Mesh::VertexVertexIter vv_it; typename Mesh::Scalar valence(0.0); mesh_.property(cog_, vh) = typename Mesh::Point(0.0, 0.0, 0.0); for (vv_it=mesh_.vv_iter(vh); vv_it; ++vv_it) { mesh_.property(cog_, vh) += mesh_.point( vv_it ); ++valence; } mesh_.property(cog_, mesh_.handle(_v) ) /= valence; } private: Mesh& mesh_; Property_cog& cog_; }; class SetCOG { public: SetCOG(Mesh& _mesh, Property_cog& _cog) : mesh_(_mesh), cog_(_cog) {} void operator()(typename Mesh::Vertex& _v) { typename Mesh::VertexHandle vh(mesh_.handle(_v)); if (!mesh_.is_boundary(vh)) mesh_.set_point( vh, mesh_.property(cog_, vh) ); } private: Mesh& mesh_; Property_cog& cog_; }; //--- private elements --- Mesh& mesh_; Property_cog cog_; }; #endif
and
#include <iostream> #include <vector> // -------------------- OpenMesh #include <OpenMesh/Core/IO/MeshIO.hh> #include <OpenMesh/Core/Mesh/Types/TriMesh_ArrayKernelT.hh> // -------------------- #include "smooth_algo.hh" // ---------------------------------------------------------------------------- #ifndef DOXY_IGNORE_THIS struct MyTraits : public OpenMesh::DefaultTraits { HalfedgeAttributes(OpenMesh::Attributes::PrevHalfedge); }; #endif typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits> MyMesh; // ---------------------------------------------------------------------------- int main(int argc, char **argv) { MyMesh mesh; // check command line options if (argc != 4) { std::cerr << "Usage: " << argv[0] << " #iterations infile outfile\n"; return 1; } // read mesh from stdin if ( ! OpenMesh::IO::read_mesh(mesh, argv[2]) ) { std::cerr << "Error: Cannot read mesh from " << argv[2] << std::endl; return 1; } // smoothing mesh argv[1] times SmootherT<MyMesh> smoother(mesh); smoother.smooth(atoi(argv[1])); // write mesh to stdout if ( ! OpenMesh::IO::write_mesh(mesh, argv[3]) ) { std::cerr << "Error: cannot write mesh to " << argv[3] << std::endl; return 1; } return 0; }