Openmesh函数库设计及与CGAL的对比

在前面写了CGAL模板类设计的一些思路,这里尝试写一点openmesh库的设计思路以及和CGAL的对比.虽然OPENMESH代码量小,不过还是只看懂皮毛,很大部分算是翻译帮助文档吧,主要用作笔记,

方便以后继续分析。

  • 相对CGAL的功能强大和庞大(包含大量计算几何算法的实现),Openmesh显得更加小巧轻量化,它更专注在三维网格数据结构的表示,

外围也提供在此基础上的网格简化算法及框架,网格细分等等应用算法。

  • 同样是在实现上大量依赖模版类,但是Openmesh源代码的可读性更好些(很大程度上也因为它的小巧,CGAL实现文件太多了,层次结构复杂),
    在提供给用户的类,
    Openmesh的设计是尽可能少的让用户去知晓模版参数,也就是除非必须,
尽量减少模版参数甚至不带模板参数。
  • Openmesh设计的一个亮点是用户可以在程序运行时动态为网格添加属性和删除属性

    这个功能很赞的。一个典型的应用,可以写一个对网格进行光顺的smooth算法
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_;
};

当用户希望对其所持有的mesh做光顺处理的时候,就可以交给SmooterT类来完成。

// smoothing mesh argv[1] times
  SmootherT<MyMesh> smoother(mesh);
  smoother.smooth(atoi(argv[1]));
这个实例也显示了openmesh和STL的结合,foreach,函数对象的使用。
最酷的还是SmootherT类会给传入的mesh对象添加顶点参数,即每一个顶点添加一个cog_,记录光顺后的位置,最后
所有顶点都计算好,原顶点位置可以改变的时候再赋值给原顶点。当完成了smooth操作后析构函数自动又把这个属性从mesh中删除,这样对于用户而言这些就是透明的,对用户而言mesh只是被光顺。
这种实现很漂亮,如果不采用动态添加属性的话,用户就需要在定义自己的mesh的时候考虑到由于光顺操作的需要,添加一个cog_属性,但是当光顺完不需要这个属性的时候,该属性也还会存在,
占用内存。
如果不想这样,那也只好在调用smooth的时候建立一个一维数组,大小和顶点数目相同,将属性值记录到该数组中,不用了删除数组占用空间。但是这样的可读性就差了。
另外一个应用,点法向的计算。(法向属性常规属性,用户也可以自由添加删除,但是调用函数和用户自定义属性有所不同)
首先可以给网格添加顶点属性即
mesh.request_vertex_normals(); 

因为计算顶点法向是需要面法相的(所有相邻面法向的和),所以我们需要添加面法向属性

  mesh.request_face_normals(); 

// let the mesh update the normals
   mesh.update_normals(); 

// dispose the face normals, as we don't need them anymore不需要面法向了就去掉它
   mesh.release_face_normals();


相当的灵活。

  • CGAL的网格模型数据结构部分(半边数据结构)的架构。

 

image

半边结构示意图。半边结构非常适合描述网格结构,因为它可以方便的改变网格拓扑形态,如删除边,分裂边等待.

image

上图为CGAL中的类结构设计.

image

各个模块关系.

默认的顶点,边,面

image

这里我们只关心,最上层的Polyhedron
用户使用默认的Polyhedron
#include <CGAL/Simple_cartesian.h>
#include <CGAL/Polyhedron_3.h>
#include <iostream>

typedef CGAL::Simple_cartesian<double>     Kernel;
typedef Kernel::Point_3                    Point_3;
typedef CGAL::Polyhedron_3<Kernel>         Polyhedron;
typedef Polyhedron::Vertex_iterator        Vertex_iterator;

int main() {
    Point_3 p( 1.0, 0.0, 0.0);
    Point_3 q( 0.0, 1.0, 0.0);
    Point_3 r( 0.0, 0.0, 1.0);
    Point_3 s( 0.0, 0.0, 0.0);

    Polyhedron P;
    P.make_tetrahedron( p, q, r, s);
    CGAL::set_ascii_mode( std::cout);
    for ( Vertex_iterator v = P.vertices_begin(); v != P.vertices_end(); ++v)
        std::cout << v->point() << std::endl;
    return 0;
}
默认情况下用的是HalfedgeDS_defualt,是基于list的,用户可以选择使用基于vector实现的半边结构。

基于vector的半边结构适合静态的网格结构。

typedef CGAL::Polyhedron_3< Traits, 
                            CGAL::Polyhedron_items_3, 
                            CGAL::HalfedgeDS_default>      Polyhedron;
#include <CGAL/Cartesian.h>
#include <CGAL/HalfedgeDS_vector.h>
#include <CGAL/Polyhedron_3.h>
#include <iostream>

typedef CGAL::Cartesian<double>                        Kernel; //CGAL用Traits(Kernel)表示采用的数据精度类型如这里的Cartesian<double> 
typedef Kernel::Point_3                                Point_3;
typedef CGAL::Polyhedron_3< Kernel,
                            CGAL::Polyhedron_items_3,
                            CGAL::HalfedgeDS_vector>   Polyhedron;  //使用基于vector实现的半边结构

int main() {
    Point_3 p( 1.0, 0.0, 0.0);
    Point_3 q( 0.0, 1.0, 0.0);
    Point_3 r( 0.0, 0.0, 1.0);
    Point_3 s( 0.0, 0.0, 0.0);

    Polyhedron P;    // alternative constructor: Polyhedron P(4,12,4);
    P.make_tetrahedron( p, q, r, s);
    CGAL::set_ascii_mode( std::cout);
    std::copy( P.points_begin(), P.points_end(),
	       std::ostream_iterator<Point_3>( std::cout, "\n"));
    return 0;
}
用户如何定义自己的特定的mesh顶点,边,面属性?
#include <CGAL/Simple_cartesian.h>
#include <CGAL/IO/Color.h>
#include <CGAL/Polyhedron_3.h>

// A face type with a color member variable.
template <class Refs>
struct My_face : public CGAL::HalfedgeDS_face_base<Refs> {
    CGAL::Color color;
};

// An items type using my face.
struct My_items : public CGAL::Polyhedron_items_3 {
    template <class Refs, class Traits>
    struct Face_wrapper {
        typedef My_face<Refs> Face;
    };
};

typedef CGAL::Simple_cartesian<double>        Kernel;
typedef CGAL::Polyhedron_3<Kernel, My_items>  Polyhedron;
typedef Polyhedron::Halfedge_handle           Halfedge_handle;

int main() {
    Polyhedron P;
    Halfedge_handle h = P.make_tetrahedron();
    h->facet()->color = CGAL::RED;
    return 0;
}

注意这种方式虽然也算方便,但是对用户而言其实他不关系Refs,Traits,最好应该能够将这些模板参数去掉,尽量少的让用户接触模板参数。Openmesh做到了这一点。

具体上面的方法怎么起到能够让程序选择使用用户自定义的结构可以看我前面关于CGAL模板类设计的文章。

  • openmesh,的设计

    • 考虑能够提供动态存储管理的基于array存储的网格。
    • 考虑提供普通的半边网格结构,和特殊的三角网格半边结构(更快,专为三角网格优化)
    • 考虑能够兼容诸如quad tree的网格表示方法,用以实现快速的细分。也可定义其它的kernal以便实现诸如允许非流形存在的网格结构。
  • Openmesh中的接口:

网格提供的可选参数

  • Face Type

        什么样的Mesh普通的还是三角网格Mesh?

  • Kernal(WELL当前版本的OPENMESH只提供基于ARRAY(std::vector)的kernal,当然kernal还是可选)

       选择采用double linked list 还是 array作为底层的存储方式?container

  • Traits

        (注意这里和CGAL的Traits表示意义不同那里是表示采用什么精度类型的数据等等,一般不需要过多考虑)

        这里通过这个参数,

                  用户可以定义它自己的顶点类型,面,边,加入自己需要的属性,方法。

        以及修改坐标的数据类型如double , float,三维坐标还是二维坐标。

 

Openmesh的用户接口API等待更加友好,可以对比下

  • 使用默认的网格结构

           使用默认的普通网格,array based

      typedef OpenMesh::PolyMesh_ArrayKernelT<>  MyMesh;
      使用默认的三角网格,array based
      typedef OpenMesh::TriMesh_ArrayKernelT<>  MyMesh;
  • 用户自定义自己的traits
// Define my personal traits
struct MyTraits : OpenMesh::DefaultTraits
{
  // Let Point and Normal be a vector of doubles
  typedef OpenMesh::Vec3d Point;   //Point的类型被定义为3维double类型
  typedef OpenMesh::Vec3d Normal; 

  // Already defined in OpenMesh::DefaultTraits
  // HalfedgeAttributes( OpenMesh::Attributes::PrevHalfedge );
  // Uncomment next line to disable attribute PrevHalfedge
  // HalfedgeAttributes( OpenMesh::Attributes::None );
  //
  // or
  //
  // HalfedgeAttributes( 0 );
}; 

#endif 

// Define my mesh with the new traits!
typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits>  MyMesh; 

// ------------------------------------------------------------------ main ---- 


  MyMesh mesh; 

  // Just make sure that point element type is double
  if ( typeid( OpenMesh::vector_traits<MyMesh::Point>::value_type ) 
       != typeid(double) )
  {
    std::cerr << "Ouch! ERROR! Data type is wrong!\n";
    return 1;
  }

//另外一个例子
struct MyTraits : public OpenMesh::DefaultTraits
{
  // store barycenter of neighbors in this member
  VertexTraits
  {
  private:
    Point  cog_;
  public:

    VertexT() : cog_( Point(0.0f, 0.0f, 0.0f ) ) { }

    const Point& cog() const { return cog_; }
    void set_cog(const Point& _p) { cog_ = _p; }
  };
};

#endif

typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits>  MyMesh;
typedef OpenMesh::TriMesh_ArrayKernelT<>          MyMesh2;
      注意和CGAL的不同这里用户自定义traits更加简单友好,没有任何的模板参数。
      以前旧版本的openmesh里面的vertex还要写成下面的形式,有模板参数,那么现在这个是如何实现的呢?
 
      template <class Base> class VertexT: public Base

 

OPENMESH同样运用了与CGAL相同的利用前置声明的技术解决类型依赖问题,见

Since the handle types depend on the containers used by the mesh
kernel (e.g. a VertexHandle may be a Vertex* or an int), the
kernel itself depends on the mesh items (in order to construct the
handle types), and the items require the handles (a vertex must store
a halfedge handle), we have to use template forward declarations
to get “safe” handle types (see [7]). Using this technique, the item
types know each other and their respective handle types, thereby
avoiding to use and cast void pointers. This also enables us to use
the handles types in the traits classes, e.g. if the face type should
contain a vertex handle。

 

不同的mesh将会是不同的类型,为了让mesh算法能够为所有类型的mesh服务,采用了泛型方法,将Mesh类型作为所有

算法的一个模板参数。

As we have seen, we use a custom–tailored mesh for each application.
All these meshes will be different C++ types. If we want
to design algorithms operating on all of these mesh types, we either
have to derive all meshes from a common virtual base class, or do
it the STL way and use generic programming methods. Since virtual
functions / classes lead to a certain overhead in space and time,
we have chosen the generic approach: every algorithm gets the type
Mesh in form of a template parameter.

 

对比一下CGAL看一下Openmesh是如何定义顶点,边和面的。

/// Definition of mesh items for use in the ArrayKernel
struct ArrayItems
{

  //------------------------------------------------------ internal vertex type

  /// The vertex item
  class Vertex
  {
    friend class ArrayKernel;
    HalfedgeHandle  halfedge_handle_;
  };


  //---------------------------------------------------- internal halfedge type

#ifndef DOXY_IGNORE_THIS
  class Halfedge_without_prev
  {
    friend class ArrayKernel;
    FaceHandle      face_handle_;
    VertexHandle    vertex_handle_;
    HalfedgeHandle  next_halfedge_handle_;
  };
#endif

#ifndef DOXY_IGNORE_THIS
  class Halfedge_with_prev : public Halfedge_without_prev
  {
    friend class ArrayKernel;
    HalfedgeHandle  prev_halfedge_handle_;
  };
#endif

  //TODO: should be selected with config.h define
  typedef Halfedge_with_prev                Halfedge;
  typedef GenProg::Bool2Type<true>          HasPrevHalfedge;

  //-------------------------------------------------------- internal edge type
#ifndef DOXY_IGNORE_THIS
  class Edge
  {
    friend class ArrayKernel;
    Halfedge  halfedges_[2];
  };
#endif

  //-------------------------------------------------------- internal face type
#ifndef DOXY_IGNORE_THIS
  class Face
  {
    friend class ArrayKernel;
    HalfedgeHandle  halfedge_handle_;
  };
};

//上面都用到了handle,

//那么handle是怎么定义的呢?

class BaseHandle;
/// Handle for a vertex entity
struct VertexHandle : public BaseHandle
{
  explicit VertexHandle(int _idx=-1) : BaseHandle(_idx) {}
};


/// Handle for a halfedge entity
struct HalfedgeHandle : public BaseHandle
{
  explicit HalfedgeHandle(int _idx=-1) : BaseHandle(_idx) {}
};


/// Handle for a edge entity
struct EdgeHandle : public BaseHandle
{
  explicit EdgeHandle(int _idx=-1) : BaseHandle(_idx) {}
};


/// Handle for a face entity
struct FaceHandle : public BaseHandle
{
  explicit FaceHandle(int _idx=-1) : BaseHandle(_idx) {}
};
 
//一个具体的kernel如何定义的呢

class ArrayKernel : public BaseKernel, public ArrayItems
{
public:

  // handles
  typedef OpenMesh::VertexHandle            VertexHandle;
  typedef OpenMesh::HalfedgeHandle          HalfedgeHandle;
  typedef OpenMesh::EdgeHandle              EdgeHandle;
  typedef OpenMesh::FaceHandle              FaceHandle;
  typedef Attributes::StatusInfo            StatusInfo;
  typedef VPropHandleT<StatusInfo>          VertexStatusPropertyHandle;
  typedef HPropHandleT<StatusInfo>          HalfedgeStatusPropertyHandle;
  typedef EPropHandleT<StatusInfo>          EdgeStatusPropertyHandle;
  typedef FPropHandleT<StatusInfo>          FaceStatusPropertyHandle;
  
  HalfedgeHandle opposite_halfedge_handle(HalfedgeHandle _heh) const
  { return HalfedgeHandle((_heh.idx() & 1) ? _heh.idx()-1 : _heh.idx()+1); } 
private:
  // iterators
  typedef std::vector<Vertex>                VertexContainer;
  typedef std::vector<Edge>                  EdgeContainer;
  typedef std::vector<Face>                  FaceContainer;
  typedef VertexContainer::iterator          KernelVertexIter;
  typedef VertexContainer::const_iterator    KernelConstVertexIter;
  typedef EdgeContainer::iterator            KernelEdgeIter;
  typedef EdgeContainer::const_iterator      KernelConstEdgeIter;
  typedef FaceContainer::iterator            KernelFaceIter;
  typedef FaceContainer::const_iterator      KernelConstFaceIter;
  typedef std::vector<uint>                  BitMaskContainer;


  KernelVertexIter      vertices_begin()        { return vertices_.begin(); }
  KernelConstVertexIter vertices_begin() const  { return vertices_.begin(); }
  KernelVertexIter      vertices_end()          { return vertices_.end(); }
  KernelConstVertexIter vertices_end() const    { return vertices_.end(); }

  KernelEdgeIter        edges_begin()           { return edges_.begin(); }
  KernelConstEdgeIter   edges_begin() const     { return edges_.begin(); }
  KernelEdgeIter        edges_end()             { return edges_.end(); }
  KernelConstEdgeIter   edges_end() const       { return edges_.end(); }

  KernelFaceIter        faces_begin()           { return faces_.begin(); }
  KernelConstFaceIter   faces_begin() const     { return faces_.begin(); }
  KernelFaceIter        faces_end()             { return faces_.end(); }
  KernelConstFaceIter   faces_end() const       { return faces_.end(); }

private:
  VertexContainer                           vertices_;
  EdgeContainer                             edges_;
  FaceContainer                             faces_;

}

 

事实上当前版本的OPENMESH的顶点,边,面的基本结构非常简单,就是相关的handle,而handle类型也是定死了的。 不存在模板循环依赖技术,与CGAL不同。

OPENMESH当前只采用基于VECTOR的构架,它把其余的顶点属性其实统统存在顶点之外,用数组保存,由于有handle即index,

所以访问的时候是用的MESH::Kernal的方法如mesh_.property(cog_, vh) += mesh_.point( vv_it ); ,mesh_根据vh找到数组中

该cog_的值。本质上是和vertex分离的,否则应该类似调用vh->cog_了,另外包括像顶点的位置,也是如此通过mesh_.point(vh)来得到

而不是CGAL中vh->point(),即坐标属性没有存储在vertex之中。vertex只存一个halfedge_handle。

所以用户用traits自定义属性如cog_或者动态添加cog_本质上和自己定义一个 cog[vertex_num]数组差不多,只不过openmesh封装了实现,提供统一方便的接口,

看上去似乎就像是给vertex本身加了个cog_域。mesh_.property(cog_, vh)还是反映了实际的存储情况。

但是问题是如果要提供基于list的mesh kernal呢,当前的架构似乎不能支持list吧?

不过对于自己定义traits的时候给vertex_traits加入的属性cog_,方法cog()是可以按照下面的方式访问的。(注,这个是老的版本这样支持的,

新版本去除了这一方式,其余的属性本质上就是顶点Vertex之外,由Mesh利用顶点属性数组统一管理的)

MyMesh::VertexIter          v_it
v_it->cog()         //旧版本
//新版本要用mesh.data(v_it)->cog()//看了一版本变化说明,似乎是说因为MS不支持模版的前置循环声明好像,不得已为之的。

mesh.data(v_it).set_cog(cog / valence);

下面是02年的修改声明。应该就是因为这个原因,就把顶点,边,面定死了,不过看CGAL应该是支持的啊,奇怪:)现在的MS VC8应该肯定支持的。

Removed cyclic dependency between items and kernel, because MS VC++ cannot handle the resulting template forward declaration :-(.

Therefore the Base::Refs class, given for the traits classes, no longer provides the types \c Vertex, \c Halfedge, \c Edge, and \c Face.

It now only provides all handle types, the point and the scalar type.

我们看下为了实现下面的应用代码,程序内部是怎么构架的。

应用代码:
struct MyTraits : public OpenMesh::DefaultTraits
{
  // store barycenter of neighbors in this member
  VertexTraits
  {
  private:
    Point  cog_;
  public:

    VertexT() : cog_( Point(0.0f, 0.0f, 0.0f ) ) { }

    const Point& cog() const { return cog_; }
    void set_cog(const Point& _p) { cog_ = _p; }
  };
};

#endif

typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits>  MyMesh; //下面就可以MyMesh mesh;使用这个mesh了。
 

//OK具体上面的代码内部是如何实现的呢。

先看DefaultTraits

//只显示部分内容

struct DefaultTraits
{
  /// The default coordinate type is OpenMesh::Vec3f.
  typedef Vec3f  Point;

  /// The default normal type is OpenMesh::Vec3f.
  typedef Vec3f  Normal;
  …
#ifndef DOXY_IGNORE_THIS
  VertexTraits    {};
  HalfedgeTraits  {};
  EdgeTraits      {};
  FaceTraits      {};
#endif
  VertexAttributes(0);
  …
};

先不管 Atrtributes,我们就看VertexTraits,

这里其实是用了宏,来对用户隐藏了模板参数。

/// Macro for defining the vertex traits. See \ref mesh_type.
#define VertexTraits \
  template <class Base, class Refs> struct VertexT : public Base   //其余Tratis 如face,halfedge的一样的方式处理
所以实际代码展开之后类似下面
struct MyTraits : public OpenMesh::DefaultTraits
{
  template <class Base, class Refs> struct VertexT : public Base
  {
    int some_additional_index;
  };
};

这也就是为什么构造函数要写VertexT()的原因。

The template argument Base provides access to the mesh handles and to the Point and Scalar type by its member class Refs. 
Adding a MyMesh::FaceHandle to the vertex class can therefore be implemented like this:
下面显示如何给顶点添加面handle属性。
struct MyTraits : public OpenMesh::DefaultTraits 
{
  VertexTraits
  {
    int some_additional_index;
    typename Base::Refs::FaceHandle my_face_handle;
  };
};
 
下面再看typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits> MyMesh;意味着什么呢?

 

TriMesh_ArrayKernelT

它的实现利用了继承TriMesh_ArrayKernel_GeneratorT<Traits>::Mesh


template <class Traits = DefaultTraits>
class TriMesh_ArrayKernelT
  : public TriMesh_ArrayKernel_GeneratorT<Traits>::Mesh
{};


template <class Traits>
struct TriMesh_ArrayKernel_GeneratorT
{
  typedef FinalMeshItemsT<Traits, true>               MeshItems;
  typedef AttribKernelT<MeshItems, TriConnectivity>  AttribKernel;
  typedef TriMeshT<AttribKernel>                      Mesh;  //其实就是最终使用的mesh类型
};

image

代码所表示的正是如上图所示的继承关系。OPENMESH大量利用 template<class Base> class Derived: public Base的继承模式。

1. The BaseKernel defines the basic operations on properties like add/remove/access.
2. Next the AttribKernelT adds the standard properties all associated methods.
3. Finally the ArrayKernelT provides the methods to add/remove/access the mesh items vertices, (half-)edges, and faces. The base class is passed as a template parameter, since depending on the underlying storage type the AttribKernel might change.

1. The PolyMeshT inherits from the kernel and provide all necessary methods to work with polygonal meshes.
2. Finally we derive TriMeshT from PolyMeshT to have an specialization for triangle meshes.

1.先来看FinalMeshItemsT,为了方便去掉了一些代码如attributes的部分。

FinalMeshItemsT<Traits, true>               MeshItems;
 
template <class Traits, bool IsTriMesh>
struct FinalMeshItemsT
{
  struct Refs
  {
    typedef typename Traits::Point            Point;                 //所以用户提供的MyTraits可能会改变Point的类型
    typedef typename vector_traits<Point>::value_type Scalar;
    …
    typedef OpenMesh::VertexHandle            VertexHandle;
  };
  //--- export Refs types ---
  typedef typename Refs::Point           Point;
  class ITraits
  {};
  
  typedef typename Traits::template VertexT<ITraits, Refs>      VertexData;     //这里用到了VertexT(可能由用户修改提供)
  typedef typename Traits::template HalfedgeT<ITraits, Refs>    HalfedgeData;
  …
};
} // namespace OpenMesh


2.再看AttribKernelT

AttribKernelT<MeshItems, TriConnectivity>  AttribKernel;通过继承Connectivity继承TriConnectivity->PolyConnectivity->ArrayKernel->BaseKernel + ArrayItems

template <class MeshItems, class Connectivity>
class AttribKernelT : public Connectivity       // class TriConnectivity : public PolyConnectivity

{                                               //class PolyConnectivity : public ArrayKernel
public:

  //---------------------------------------------------------------- item types

  typedef typename Connectivity::Vertex     Vertex;
  typedef typename Connectivity::Halfedge   Halfedge;
  typedef typename Connectivity::Edge       Edge;
  typedef typename Connectivity::Face       Face;

  typedef typename MeshItems::Point         Point;
  typedef typename MeshItems::Normal        Normal;
  …
  typedef typename MeshItems::VertexData    VertexData;
  typedef typename MeshItems::HalfedgeData  HalfedgeData;
  typedef typename MeshItems::EdgeData      EdgeData;
  typedef typename MeshItems::FaceData      FaceData;

  typedef AttribKernelT<MeshItems,Connectivity>  AttribKernel;

  enum Attribs  {
    VAttribs = MeshItems::VAttribs,
    HAttribs = MeshItems::HAttribs,
    EAttribs = MeshItems::EAttribs,
    FAttribs = MeshItems::FAttribs
  };

  typedef VPropHandleT<VertexData>              DataVPropHandle;    
  typedef HPropHandleT<HalfedgeData>            DataHPropHandle;
  typedef EPropHandleT<EdgeData>                DataEPropHandle;
  typedef FPropHandleT<FaceData>                DataFPropHandle;

public:

  //-------------------------------------------------- constructor / destructor

  AttribKernelT()
  : refcount_vnormals_(0),
    refcount_vcolors_(0),
    …
   {
    add_property( points_, "v:points" );

    if (VAttribs & Attributes::Normal)
      request_vertex_normals();
    …
    //FIXME: data properties might actually cost storage even
    //if there are no data traits??
    add_property(data_vpph_);
    add_property(data_fpph_);
    add_property(data_hpph_);
    add_property(data_epph_);
  }

  virtual ~AttribKernelT()
  {
    // should remove properties, but this will be done in
    // BaseKernel's destructor anyway...
  }

  /** Assignment from another mesh of \em another type.
      \note All that's copied is connectivity and vertex positions.
      All other information (like e.g. attributes or additional
      elements from traits classes) is not copied.
      \note If you want to copy all information, including *custom* properties,
      use PolyMeshT::operator=() instead.
      TODO: version which copies standard properties specified by the user
  */
  template <class _AttribKernel>
  void assign(const _AttribKernel& _other)
  {
    assign_connectivity(_other);
    for (typename Connectivity::VertexIter v_it = Connectivity::vertices_begin();
         v_it != Connectivity::vertices_end(); ++v_it)
    {//assumes Point constructor supports cast from _AttribKernel::Point
      set_point(v_it, (Point)_other.point(v_it));
    }
  }
  …
  //------------------------------------------------ request / alloc properties

  void request_vertex_normals()
  {
    if (!refcount_vnormals_++)
      add_property( vertex_normals_, "v:normals" );
  }

  …
  //------------------------------------------------- release / free properties

  void release_vertex_normals()
  {
    if ((refcount_vnormals_ > 0) && (! --refcount_vnormals_))
      remove_property(vertex_normals_);
  }
  ….
  //---------------------------------------------- dynamic check for properties

  bool has_vertex_normals()       const { return vertex_normals_.is_valid();      }
  public:

  typedef VPropHandleT<Point>               PointsPropertyHandle;
  typedef VPropHandleT<Normal>              VertexNormalsPropertyHandle;
  …
public:
  //standard vertex properties
  PointsPropertyHandle                      points_pph() const
  { return points_; }
  …
  VertexData&                               data(VertexHandle _vh)
  { return property(data_vpph_, _vh); }

  …
private:
  //standard vertex properties
  PointsPropertyHandle                      points_;
  VertexNormalsPropertyHandle               vertex_normals_;
  …
  //data properties handles
  DataVPropHandle                           data_vpph_;
  DataHPropHandle                           data_hpph_;
  DataEPropHandle                           data_epph_;
  DataFPropHandle                           data_fpph_;
  …
};

关于add_property在class BaseKernel中。

template <class T>
void add_property( VPropHandleT<T>& _ph, const std::string& _name="<vprop>")
{
  _ph = VPropHandleT<T>( vprops_.add(T(), _name) );
  vprops_.resize(n_vertices());         //in BaseKernel  PropertyContainer  vprops_;
}

 

//

class ArrayKernel : public BaseKernel, public ArrayItems
{
public: 

  // handles
  typedef OpenMesh::VertexHandle            VertexHandle;
  typedef OpenMesh::HalfedgeHandle          HalfedgeHandle;
  typedef OpenMesh::EdgeHandle              EdgeHandle;
  typedef OpenMesh::FaceHandle              FaceHandle;
  typedef std::vector<Vertex>                VertexContainer;
 
Vertex& vertex(VertexHandle _vh)
{
  assert(is_valid_handle(_vh));
  return vertices_[_vh.idx()];
}
private:
VertexContainer                           vertices_;
  EdgeContainer                             edges_;
  FaceContainer                             faces_;
}
class BaseKernel
{
private: 
  PropertyContainer  vprops_;
  PropertyContainer  hprops_;
  PropertyContainer  eprops_;
  PropertyContainer  fprops_;
  PropertyContainer  mprops_;
};


3.最后看TriMeshT

TriMeshT<AttribKernel>                      Mesh;

template <class Kernel>
class TriMeshT : public PolyMeshT<Kernel>
{
public:
  // self
  typedef TriMeshT<Kernel>                      This;
  typedef PolyMeshT<Kernel>                     PolyMesh;
  //--- items ---
  typedef typename PolyMesh::Scalar             Scalar;
  typedef typename PolyMesh::Point              Point;
  …
  //--- handles ---
  typedef typename PolyMesh::VertexHandle       VertexHandle;
  typedef typename PolyMesh::HalfedgeHandle     HalfedgeHandle;
  typedef typename PolyMesh::EdgeHandle         EdgeHandle;
  typedef typename PolyMesh::FaceHandle         FaceHandle;

  //--- iterators ---
  typedef typename PolyMesh::VertexIter         VertexIter;
  …
  //--- circulators ---
  typedef typename PolyMesh::VertexVertexIter         VertexVertexIter;
  …
}
 

template <class Kernel>
class PolyMeshT : public Kernel 

 

看一下具体执行Mesh mesh;的时候构造函数的调用,其中PorpertyContainer是BaseKernel类的一个成员变量被初始化,其余都是代表类的默认初始化。可以看出类的层次关系。

(gdb) bt
#0  new_allocator (this=0xbfc56563) at /usr/include/c++/4.2/ext/new_allocator.h:68
#1  0xb7f4f07b in allocator (this=0xbfc56563) at /usr/include/c++/4.2/bits/allocator.h:100
#2  0xb7f50a81 in PropertyContainer (this=0xbfc566ac) at /home/allen/study/OpenMesh/src/OpenMesh/Core/../../OpenMesh/Core/Utils/PropertyContainer.hh:66
#3  0xb7f56a76 in BaseKernel (this=0xbfc566a8) at /home/allen/study/OpenMesh/src/OpenMesh/Core/../../OpenMesh/Core/Mesh/BaseKernel.hh:95
#4  0xb7f4ce46 in ArrayKernel (this=0xbfc566a8) at /home/allen/study/OpenMesh/src/OpenMesh/Core/Mesh/ArrayKernel.cc:49
#5  0x0805854f in PolyConnectivity (this=0xbfc566a8) at ../../../../src/OpenMesh/Core/Mesh/PolyConnectivity.hh:164
#6  0x0805856d in TriConnectivity (this=0xbfc566a8) at ../../../../src/OpenMesh/Core/Mesh/TriConnectivity.hh:55
#7  0x08061720 in AttribKernelT (this=0xbfc566a8) at ../../../../src/OpenMesh/Core/Mesh/AttribKernelT.hh:124
#8  0x08061c21 in PolyMeshT (this=0xbfc566a8) at ../../../../src/OpenMesh/Core/Mesh/PolyMeshT.hh:181
#9  0x08061c3f in TriMeshT (this=0xbfc566a8) at ../../../../src/OpenMesh/Core/Mesh/TriMeshT.hh:164
#10 0x08061c5d in TriMesh_ArrayKernelT (this=0xbfc566a8) at ../../../../src/OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh:93
#11 0x080503fa in main (argc=1, argv=0xbfc56934) at smooth.cc:50

 

TODO

1.关于动态添加属性,究竟是如何实现的

template <class T>
typename VPropHandleT<T>::reference
property(VPropHandleT<T> _ph, VertexHandle _vh) {
   return vprops_.property(_ph)[_vh.idx()];
}

vprops_是PropertyContainer类型的

mesh.property(cogs,v_it) += mesh.point( vv_it );

调用 property(cogs,v_it)    ->调用 vprops_.property(cogs)[v_it.idx()]

  template <class T> PropertyT<T>& property(BasePropHandleT<T> _h)
  {
    assert(_h.idx() >= 0 && _h.idx() < (int)properties_.size());
    assert(properties_[_h.idx()] != NULL);
#ifdef OM_FORCE_STATIC_CAST
    return *static_cast  <PropertyT<T>*> (properties_[_h.idx()]);
#else
    PropertyT<T>* p = dynamic_cast<PropertyT<T>*>(properties_[_h.idx()]);
    assert(p != NULL);
    return *p;
#endif
  }

大概是mesh根据cogs找到它的属性数组,然后利用v_it的顶点索引,找到具体的值,

不过太复杂了,有时间再弄清细节吧。

2.用array的话,如何能够在拓扑变化的情况下,如点的删除,保证高效率的。

3.简化和细分模块是如何架构的。

posted @ 2009-10-13 18:57  阁子  阅读(12134)  评论(2编辑  收藏  举报