使用標準模板庫算法
由於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;
}