CGAL幾何庫 半邊網格數據結構 模板類 設計核心思想

    CGAL是一個優秀的幾何處理庫,對於三維網格採用半邊格式存儲。

      其實對於網格而言,無外乎定義它的邊,頂點,面,數據存儲。

      問題是用戶可能會有不同的需求,比如做模型簡化,需要對每個頂點加一個cost域,而對其它應用則不需要,

也許你會說可以給基本的定點數據結構加一個指針,用戶自己定義其它的數據都由該指針指向,但這種設計並不好。

 顯然模板化處理是更好的方法,不要把頂點類型定死。

      這樣一來我們可以通過讓一個網格類,擁有不同的頂點類型,邊類型,面類型,具體類型由用戶決定,用戶可以通過

繼承底層提供的基本類型,加入自己 的需要的屬性,方法,生成新的類型,作爲template參數,並傳給網格類。從而實現

了網格的靈活設計。 

       CGAL就是採用了這一方法,但是它有大量的模板技巧,wrapper class 包裝等等,從而對於想要了解底層具體實現的而言

顯得比較複雜難於理解,雖然它的使用還是很簡單方便的。 下面我將根據參考文獻 Using Generic Programming for Designing a Data Structure for Polyhedral Surface

介紹其模板類設計的非常簡潔的核心的思想,並給出代碼實例,注意和實際CGAL實現代碼是有出入的,這裏做了簡化

(比如CGAL對頂點,邊,面數據結構進行了wrapper class封裝,使得wrapper class沒有任何模板參數),但是思想是一致的。 


      這裏爲了簡單,去掉面,僅僅考慮邊和頂點,對於頂點我們要記錄它的對應的半邊信息,對於半邊我們也要記錄頂點信息,

      問題是對於特定的item type,比如頂點,它不知道其相關聯的其它item的具體類型,如半邊的具體類型 。 

 

      在CGAL的設計中,vertex頂點,通過一個placeholder來獲得其它item的類型信息。(在下面的小程序演示中,HalfedgeDS作爲那個placeholder,存儲所有iterm的類型信息)

 

     CGAL將所有的局部類型信息,如頂點,半邊,面放在一個單一的模板參數Refs中。

     在頂點中僅僅用到 Halfedge_handel作爲對應的halfedge的引用。

     其他如半邊,面的設計類似。

     對於整體的半邊數據結構,它將會由如頂點,半邊,面的類型來模板化,(parameterized with the item types)

     但是item types如頂點,已經是 class template模板類了,所以我們需要 template as template arguments 即模板參數本身是模板類

 

這種方法還是很常見的,比如考慮二叉樹的設計,允許二叉樹類裝配不同類型的節點,即節點是模板參數

而節點類本身允許有不同的存儲數據類型,也就是說節點本身就是模板類。 

可以採用下面的設計 

二叉樹節點類

template <typename ElemType>
class Node {
       private:
            ElemType m_data; 
}; 

 二叉樹類

template  <template<type Elem> class Node, typename U>
class BinaryTree {
 
       private:
            Node<U>  *m_root; 
}; 

 具體使用

BinaryTree< Node, int> MyBinaryTree;

即可。 

 

好回到CGAL中,這 裏的類型依賴構成了一個循環,半邊數據結構需要vertex,halfedge類型參數的實化,而半邊數據結構類halfedge ds知道 handle 類型,可以用做Refs 參數的實際類型, 儘管當前handles還沒有被聲明(vertex,halfedge同樣需要半邊數據結構類來實化)。聲明和定義的不同使得這一切成爲可能。

struct Edge;
struct Node {
Edge * edge;
// .... maybe more than one edge ....
};
struct Edge {
Node * source;
Node * dest;
};
//最簡單的一個示例
template <class Graph>
struct Node {
typedef typename Graph::Edge Edge;
Edge* edge;
// .... maybe some more edges ....
};
template <class Graph>
struct Edge {
typedef typename Graph::Node Node;
Node* node;
};
template < template <class G> class T_Node,
template <class G> class T_Edge>
struct Graph {
typedef Graph< T_Node, T_Edge>       Self;
typedef T_Node<Self>                 Node;
typedef T_Edge<Self>                 Edge;
};
int main() {
typedef Graph< Node, Edge> G;
G::Node node;
G::Edge edge;
node.edge = &edge;
edge.node = &node;
}

template <class Graph>
struct Colored_node : public Node<Graph> {
int color;
};
int main() {
typedef Graph< Colored_node, Edge> G;
G::Node node;
G::Edge edge;
node.edge = &edge;
edge.node = &node;
node.color = 3;
}

 注意上面代碼用的是指針,本質上還是利用前置聲明,如果改爲Edge edge,就不行了,因爲那樣編譯器需要知道Edge的定義,前置聲明不行。

It is important to understand that these cyclic definitions work -- as for the C example -- because we can make use of a declared type to define pointers and references to this type before this type is defined itself. For example, we cannot change the pointer member Edge * edge  of the node class to a value Edge edge .


 好了,說了這麼多看實際的代碼吧。

我寫了個小程序測試了一下,爲了測試加入了顯示PrintName的代碼。

 //simple_cgal.h
#include <list>
#include <string>
#include <iostream>
using std::list;
using std::string;
using std::cout;
using std::endl;
template <typename Refs>
struct Vertex {
      typedef typename Refs::Halfedge_handle Halfedge_handle;
      Vertex(string name = "vertex0") { m_name = name;}
      void PrintName() const {
         cout << "This is " << m_name << endl;
       }
      Halfedge_handle halfedge() const { return h; }
      void set_halfedge (Halfedge_handle g) { h = g; }
private:
      Halfedge_handle h;
      string m_name;
};
template <typename Refs>
struct Halfedge {
      typedef typename Refs::Vertex_handle Vertex_handle;
      Halfedge(string name = "halfedge0") { m_name = name;}
      void PrintName() const {
            cout << "This is " << m_name << endl;
      }
      Vertex_handle vertex() const { return v;}
      void set_vertex (Vertex_handle vv) { v = vv;}
private:
      Vertex_handle v;
      string m_name;
};
template < template <typename Ref> class Vertex, template <typename Ref> class Halfedge>
struct HalfedgeDS {
      typedef HalfedgeDS< Vertex, Halfedge> Self;
      typedef Vertex<Self>                  V;
      typedef Halfedge<Self>                H;
      typedef list<V>                       Vlist;
      typedef list<H>                       Hlist;
      typedef typename Vlist::iterator Vertex_handle;
      typedef typename Hlist::iterator Halfedge_handle;
};
 //test_simple_cgal.cc
#include "simple_cgal.h"
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
    typedef HalfedgeDS< Vertex, Halfedge> HDS;
    typedef HDS::V Vert;
    typedef HDS::H HalfEdge;
    typedef HDS::Vlist Vlist;
    typedef HDS::Hlist Hlist;
    Vert v("vertex a");
    HalfEdge h("Halfedge b");
    Vlist vlist;
    Hlist hlist;
    vlist.push_back(v);
    hlist.push_back(h);
    v.set_halfedge(hlist.begin());
    h.set_vertex(vlist.begin());
    v.halfedge()->PrintName();
    h.vertex()->PrintName();
    return 0;
}
運行結果:
This is Halfedge b This is vertex a CGAL中實際的定義對於頂點,邊和麪元素進行了封裝,將他們放在同一個不帶模板參數的類裏面,類似下面代碼模仿的HalfedgeDS_iems, 從而將他們集中到一起並且最外層去掉模板參數,裏面通過wrapper將他們的定義包裝。用戶可以定義不同的頂點類,只要 在傳給HalfedgeDS的HalfedgeItesms參數的類中typedef 自己定義的頂點類爲Vertex即可。 //簡單模擬vertex,halfedge wrapper用法演示 //simple_cgal2.h
#include <list>
#include <string>
#include <iostream>
using std::list;
using std::string;
using std::cout;
using std::endl;
template <typename Refs>
struct Vertex {
      typedef typename Refs::Halfedge_handle Halfedge_handle;
      Vertex(string name = "vertex0") { m_name = name;}
      void PrintName() const {
            cout << "This is " << m_name << endl;
      }
      Halfedge_handle halfedge() const { return h; }
      void set_halfedge (Halfedge_handle g) { h = g; }
   private:
      Halfedge_handle h;
      string m_name;
};
template <typename Refs>
struct Halfedge {
      typedef typename Refs::Vertex_handle Vertex_handle;
      Halfedge(string name = "halfedge0") { m_name = name;}
      void PrintName() const {
            cout << "This is " << m_name << endl;
      }
      Vertex_handle vertex() const { return v;}
      void set_vertex (Vertex_handle vv) { v = vv;}
   private:
      Vertex_handle v;
      string m_name;
};
struct HalfedgeDS_iems {
      template <class Refs>
      struct Vertex_wrapper {
            typedef Vertex<Refs> Vertex;
      };
      template <class Refs>
      struct Halfedge_wrapper {
            typedef Halfedge<Refs> Halfedge;
            };
};
template < typename HalfedgeDSItems>
struct HalfedgeDS {
      typedef HalfedgeDS< HalfedgeDSItems> Self;
      typedef HalfedgeDSItems                            Items;
      typedef typename Items::template Vertex_wrapper<Self>                                                                                                            Vertex_wrapper;
            typedef typename Items::template Halfedge_wrapper<Self>
Halfedge_wrapper;
            typedef typename Vertex_wrapper::Vertex            Vertex;
            typedef typename Halfedge_wrapper::Halfedge        Halfedge;
            typedef list<Vertex>                       Vlist;
            typedef list<Halfedge>                       Hlist;
            typedef typename Vlist::iterator Vertex_handle;
            typedef typename Hlist::iterator Halfedge_handle;
};

//test_simgple_cgal2.cc
#include "simple_cgal2.h"
#include <iostream>

using namespace std;

int main(int argc, char *argv[])

{

      typedef HalfedgeDS<HalfedgeDS_iems> HDS;

      typedef HDS::Vertex Vert;

      typedef HDS::Halfedge HalfEdge;

      typedef HDS::Vlist Vlist;

      typedef HDS::Hlist Hlist;

      Vert v("vertex a");

      HalfEdge h("Halfedge b");

      Vlist vlist;

      Hlist hlist;

      vlist.push_back(v);

      hlist.push_back(h);

      v.set_halfedge(hlist.begin());

      h.set_vertex(vlist.begin());

      v.halfedge()->PrintName();

      h.vertex()->PrintName();

      return 0;

}


運行結果和上面程序一致。多了一個HalfedgeDS_iems包裝的一個好處是由於HalfedgeDS_iems沒有模板參數,所以下面的定義是可能的,CGAL中定義有Polyhedron它的一個模板參數是HDS,即半邊結構,考慮上面沒有封裝到HalfedgeItesms的方案,
template < template <typename Ref> class Vertex, template <typename Ref> class Halfedge>
struct HalfedgeDS
HalfedgeDS的定義已經有兩級模板參數了,如果再被傳遞會有三級模板參數,C++是不允許這樣的,所以HalfedgeDS_iems方法不失爲一種繞過的技巧。關於Polyhedron_items<Items>下面給予介紹。先看一個用戶自己擴充面的例子,完全繼承已有的邊和頂點,用戶當然也可以全部擴充,總之通過wrapper是很靈活的。
template < class Traits,

           class Items = Items_default,

           template < class, class> calss HDS = HalfedgeDS_default>

struct Polyhedron {

      typedef Polyhedron_items<Items> Deerived_items;

      typedef HDS< Traits, Derived_items> HDS;

      typedef typename HDS::Vertex                  Vertex;

      typedef typename HDS::Halfedge                Halfedge;

      typedef typename HDS::Face                    Face;



.....

};
template <class Refs, class Triats>

class My_facet : public CGAL_HalfedgeDS_facet_base<Refs> {

      int face_cost;  // we need to sore addtional info 

    public:

       typename Traits::Vector_3 normal;  

};

struct My_items : public CGAL_Polyhedron_items_3 {

      template <class Refs, class Traits>

      struct Face_wrapper {

            typedef My_facet< Refs, Traits> Facet;

      };

};

typedef CGAL_Polyhedron_3<Traits, My_items> Polyhedron;
OK,用戶使用起來就是這麼簡單,這樣我們的Polyhedron就是應用用戶定義的facet了同時保留了CGAL_Polyhedron_items_3中定義的facet的所以屬性函數,如set_handle.由於用戶沒有定義Vertex和Halfedge所以它們完全用CGAL_Polyhedron_items_3中提供的Vertex和Halfege不加任何擴充。//CGAL源代碼
class Polyhedron_items_3 {

public:

    template < class Refs, class Traits>

    struct Vertex_wrapper {

        typedef typename Traits::Point_3 Point;

        typedef HalfedgeDS_vertex_base< Refs, Tag_true, Point> Vertex;

    };

    template < class Refs, class Traits>

    struct Halfedge_wrapper {

        typedef HalfedgeDS_halfedge_base< Refs>                Halfedge;

    };

    template < class Refs, class Traits>

    struct Face_wrapper {

        typedef typename Traits::Plane_3 Plane;

        typedef HalfedgeDS_face_base< Refs, Tag_true, Plane>   Face;

    };

};

template < class Refs >

class HalfedgeDS_face_base< Refs, Tag_true, Tag_false> {

public:

    typedef Refs                                 HalfedgeDS;

    typedef HalfedgeDS_face_base< Refs, Tag_true, Tag_false>   Base;

    typedef Tag_true                             Supports_face_halfedge;

    typedef typename Refs::Vertex_handle         Vertex_handle;

    typedef typename Refs::Vertex_const_handle   Vertex_const_handle;

    typedef typename Refs::Halfedge_handle       Halfedge_handle;

    typedef typename Refs::Halfedge_const_handle Halfedge_const_handle;

    typedef typename Refs::Face_handle           Face_handle;

    typedef typename Refs::Face_const_handle     Face_const_handle;

    typedef typename Refs::Vertex                Vertex;

    typedef typename Refs::Halfedge              Halfedge;

    // Additional tags required by Polyhedron.

    typedef Tag_false                            Supports_face_plane;

    struct Plane_not_supported {};

    typedef Plane_not_supported                  Plane;

    // No longer required.

    //typedef Tag_false                            Supports_face_normal;

private:

    Halfedge_handle hdg;

public:

    Halfedge_handle       halfedge()                        { return hdg; }

    Halfedge_const_handle halfedge() const                  { return hdg; }

    void                  set_halfedge( Halfedge_handle h)  { hdg = h; }

};
這裏面實現還是有另外的技巧,原文章中又以vertex舉例了,就按照vertex繼續吧。
template <class Vertex_base>

struct Polyhedron_vertex : public Vertex_base {

      typedef Vertex_base Base;

private:

      void set_halfedge(typename Base::Halfedge_handle g) {

            Base::set_halfedge(g);

      }

};

template <class Items>

struct Polyhderon_items {

      template <class Refs, class Traits>

      struct Vertex_wrapper {

            typedef typename Items::Vertex_wrapper<Refs, Traits> Wrapper;

            typedef typename Wrapper::Vertex Vertex_base;

            typedef Polyhedron_vertex<Vertex_base> Vertex;

      };

      //similar for facet and halfedge

};



template < class Traits,

           class Items = Items_default,

           template < class, class> calss HDS = HalfedgeDS_default>

struct Polyhedron {

      typedef Polyhedron_items<Items> Deerived_items;

      typedef HDS< Traits, Derived_items> HDS;

      typedef typename HDS::Vertex                  Vertex;

      typedef typename HDS::Halfedge                Halfedge;

      typedef typename HDS::Face                    Face;



.....

};

用戶自定義的Items,Vetex會被當作模板參數傳遞如下。也就是說最後實際用的是Polyhedron_items<Items> 中定義的Vertex Polyhedron_vertex<Vertex_base>而 Items即爲用戶定義並傳遞的點面邊數據類型類。Vertex_base是用戶定義的Vertex類型。下面看一下CGAL實際的代碼,這裏的I_Polyhedron_vertex其實就是上面的Polyhedron_vertex。

template <class VertexBase>

class I_Polyhedron_vertex  : public VertexBase  {

public:

    typedef VertexBase                            Base;

    //typedef typename Base::HalfedgeDS              HDS;

    typedef typename Base::Point                   Point;

    typedef Point                                  Point_3;



    // Handles have to explicitly repeated, although they are derived

    typedef typename Base::Vertex_handle           Vertex_handle;

    typedef typename Base::Halfedge_handle         Halfedge_handle;

    typedef typename Base::Face_handle             Face_handle;

    typedef typename Base::Face_handle             Facet_handle;

    typedef typename Base::Vertex_const_handle     Vertex_const_handle;

    typedef typename Base::Halfedge_const_handle   Halfedge_const_handle;

    typedef typename Base::Face_const_handle       Face_const_handle;

    typedef typename Base::Face_const_handle       Facet_const_handle;

    typedef typename Base::Halfedge                Halfedge;

    typedef typename Base::Face                    Face;

    typedef typename Base::Face                    Facet;



    // Supported options by HDS.

    typedef typename Base::Supports_vertex_halfedge

                                                  Supports_vertex_halfedge;

    typedef typename Base::Supports_vertex_point  Supports_vertex_point;



    // Circulator category.

    typedef typename Halfedge::Supports_halfedge_prev  Supports_prev;



public:

    // Circulator category.

    typedef HalfedgeDS_circulator_traits<Supports_prev> Ctr;

    typedef typename Ctr::iterator_category circulator_category;



    // Circulators around a vertex and around a facet.

    typedef I_HalfedgeDS_facet_circ< Halfedge_handle, circulator_category>

                                         Halfedge_around_facet_circulator;



    typedef I_HalfedgeDS_vertex_circ< Halfedge_handle, circulator_category>

                                        Halfedge_around_vertex_circulator;



    typedef I_HalfedgeDS_facet_circ<

        Halfedge_const_handle,

        circulator_category>       Halfedge_around_facet_const_circulator;



    typedef I_HalfedgeDS_vertex_circ<

        Halfedge_const_handle,

        circulator_category>      Halfedge_around_vertex_const_circulator;







    typedef typename Halfedge_around_vertex_circulator::size_type

        size_type;

    typedef typename Halfedge_around_vertex_circulator::difference_type

        difference_type;



public:

    // We need to repeat the constructors here.

    I_Polyhedron_vertex() {}

    I_Polyhedron_vertex( const VertexBase& b) : VertexBase(b) {}

    I_Polyhedron_vertex( const Point_3& p) : VertexBase(p) {}



// New Access Functions (not provided in VertexBase).



    Halfedge_around_vertex_circulator vertex_begin() {

        // a circulator of halfedges around the vertex (clockwise).

        return Halfedge_around_vertex_circulator( this->halfedge());

    }

    Halfedge_around_vertex_const_circulator vertex_begin() const {

        // a circulator of halfedges around the vertex (clockwise).

        return Halfedge_around_vertex_const_circulator( this->halfedge());

    }



    // the degree of the vertex, i.e., edges emanating from this vertex 

    std::size_t vertex_degree() const { 

        return this->halfedge()->vertex_degree(); 

    }

    size_type degree() const { return vertex_degree(); } //backwards compatible



    // returns true if the vertex has exactly two incident edges

    bool is_bivalent() const { return  this->halfedge()->is_bivalent(); }



    // returns true if the vertex has exactly three incident edges

    bool is_trivalent() const { return  this->halfedge()->is_trivalent(); }



    // No longer hidden. Now the restricted version with precondition.

    // sets incident halfedge to h. Precondition: h is incident, i.e.,

    // h->vertex() == v.

    void  set_halfedge( Halfedge_handle hh) {

        CGAL_assertion( &*(hh->vertex()) == this);

        Base::set_halfedge(hh);

    }

};
//所有這些內容對應的具體CGAL源代碼參照/usr/local/include/CGAL 
Polyhedron_3.h
HalfedgeDS_vector.h //vecotor和list思路一致不過是採用不同的內部數據組織,vector或者listHalfedgeDS_list.hPolyhedron_items_3.hHalfedgeDS_halfedge_base.h




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章