Main Page | Modules | Namespace List | Class Hierarchy | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

Using STL algorithms

Since the OpenMesh iterators are (almost) conformant to STL iterators, one can apply the STL algorithms on meshes. The following example shows how to use the STL 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;
}

acg pic Project OpenMesh, ©  Computer Graphics Group, RWTH Aachen. Documentation generated using doxygen .