原文出处:http://openmesh.org/Documentation/OpenMesh-Doc-Latest/tutorial.html
因为OpenMesh迭代器几乎与STL的迭代器一致,意味着可以对网格使用STL的算法。
下列的示例代码展示了如何使用STL算法中的for_each,因为这样写可读性更好而比手写的循环效率更高。
我们定义一个类提供平滑算法,因此定义一个可重用的组件。这个类必须是模板类,因为这里没有OpenMesh类,却有很多类型的OpenMesh:
template <class Mesh> class SmootherT
SmootherT有两个函数,一个计算给定顶点的重心,另一个设置顶点的位置到相应的重心处。一个函数就是一个简单的类有一个操作符()。第一个函数ComputeCOG计算重心并把重心保存到顶点的属性中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;
}
注意,ComputeCOG需要访问网格对象和属性句柄。这里都是平滑器对象成员变量的引用。
第二个函数类SetCOG是构造类比,设定顶点的位置。
使用这个函数和std::for_each可以实现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_));
}
}
完整代码如下:
#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
以及
#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;
}