使用标准模板库算法
由于OpenMesh迭代器(几乎)是于标准模板库(STL)迭代器一致的,因此,可以将标准模板库的算法应用于mesh。
下面的例子演示了如何使用标准模板库中的for_each结构,而这种结构要比手写循环高效的多。
我们将定义一个类,这个类提供mesh的光滑算法,然后定义一个可重用组件。由于STL中没有类似于OpenMesh这样的类,因此,我们要定义的这个类必须是模板类:
template <class Mesh> class SmootherT
SmootherT类有两个函数,一个用于计算指定点单环领域节点的重心,第二个将节点位置设置为相关的重心位置。仿函数是一种类似于operator()(...)形式的操作符重载函数。第一个仿函数ComputeCOG计算重心位置,并存储于自定义属性cog_中:
void operator()(consttypename Mesh::VertexHandle& _vh)
{
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.is_valid(); ++vv_it)
{
mesh_.property(cog_, _vh) += mesh_.point(*vv_it );
++valence;
}
mesh_.property(cog_, _vh ) /= valence;
}
注意:ComputeCOG函数需要访问mesh对象及其属性句柄。这两者都是smoother对象的成员变量的引用。
第二个仿函数classSetCOG,用于设置节点位置,构造方法类似。
使用这些仿函数均实现了对标准模板库中的std::for_each循环形式的应用。
void smooth(unsignedint _iterations)
{
for (unsignedint 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_));
}
}
完整代码如下:
#include <algorithm>
#include<OpenMesh/Core/Utils/Property.hh>
#ifndef DOXY_IGNORE_THIS
template <class Mesh> class SmootherT
{
public:
typedeftypename 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(unsignedint _iterations)
{
for (unsignedint 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()(consttypename Mesh::VertexHandle& _vh)
{
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.is_valid(); ++vv_it)
{
mesh_.property(cog_, _vh) += mesh_.point(*vv_it );
++valence;
}
mesh_.property(cog_, _vh ) /= valence;
}
private:
Mesh& mesh_;
Property_cog& cog_;
};
class SetCOG
{
public:
SetCOG(Mesh& _mesh, Property_cog&_cog)
: mesh_(_mesh), cog_(_cog)
{}
void operator()(consttypename Mesh::VertexHandle& _vh)
{
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/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;
}