CGAL代碼閱讀跳坑指南


CGAL代碼閱讀跳坑指南

整體框架介紹

generic_cgal.png

CGAL中的算法和數據結構由它們使用的對象類型和操作參數化。它們可以處理滿足特定語法和語義需求的任何具體模板參數。為了避免長參數列表,參數類型被收集到一個單獨的類中,稱為CGAL中的traits類Concept是由一組需求定義的類型的抽象。概念是由一組需求定義的類型的抽象。如果任何具體類型滿足與概念相對應的一組需求,則稱為這個概念對應的Model。使用這個術語,我們可以說CGAL算法或數據結構帶有一個traits概念,並且可以與這個概念的任何具體traits模型一起使用。

CGAL定義了geometry kernel的概念。理想情況下,這個概念的任何模型都可以與任何CGAL算法一起使用。當然,只有當一個算法或數據結構對其traits類的需求被核心概念所包含時,也就是說,如果一個算法或數據結構沒有核心概念定義中未涵蓋的特殊需求,這種情況才成立。目前,CGAL提供了一個基本geometry kernel的概念,它定義了各種幾何對象,如點、線段、直線、圓和對它們的操作,以及另外兩個概念,circular kernelspherical kernel。最后兩個內核的目標是在平面上的圓和圓弧上指定一大組功能(circular kernel),並為生活在三維球體上的圓、圓弧(spherical kernel)指定類似的功能。

CGAL目前為CGAL 2D和3D內核概念提供了幾個模型,為2D圓形和3D球形內核概念提供了一個模型。。它們再次被參數化,並且在幾何對象的表示和所涉及類型的可交換性方面有所不同。在前四種情況下,內核由數字類型參數化,數字類型用於存儲坐標,決定內核原語的基本算法。

在最后兩種情況下,圓核和球核也由Algebraic Kernel參數化,這與代數基礎一起,是CGAL中第三個明顯的高階泛化。CGAL中的代數基礎是表示代數結構的一組概念,由傳統代數中著名的對應概念驅動。代數基礎決定每個代數結構的運算、它們的屬性(例如,它們是精確的還是近似的)以及它們之間的互操作性。Algebraic Kernel負責為CGAL算法中使用的geometry kernel或特征類所需的代數操作提供抽象。目標是能夠構造、比較和執行多項式方程的實根運算。根據用於確定根的多項式的變量數量,有不同的概念(目前有單變量和雙變量代數核的概念),以及針對庫的特定幾何高層(如圓形和球形核)的專門概念。這些概念每個概念至少有一個模型。

CGAL中還有更多的互補層。最基本的層是配置層。此層負責根據安裝期間運行的測試的結果設置配置標志。支持庫層在支持庫手冊中有文檔記錄,其中包含處理CGAL中的可視化、數字類型、流和STL擴展等內容的包。

以上內容是對官方文檔中介紹的翻譯,見:https://doc.cgal.org/latest/Manual/devman_intro.html#secoverall_design

個人理解

Traits、Concept:都是用來定義一組滿足特定語法和語義需求的概念(參數類型的集合);

Model:是對於這些參數類型的實現,對Kernel進行一定的封裝;

Kernel:是最終概念具體實現的地方;

Algorithm:指的是利用概念提供的信息,完成對應的實現;

示例分析

下面以https://doc.cgal.org/latest/Convex_hull_2/classCGAL_1_1Convex__hull__traits__2.html為例驗證上面的理解是不是有問題的,下面不考慮算法到底是怎么實現的。

Concepts

https://doc.cgal.org/latest/Convex_hull_2/classConvexHullTraits__2.html

定義中說明了convex hull and extreme point算法被參數化為Traits類,定義了算法需要的對象和判斷的方法。其中類型主要包括:Point_2, Equal_2, Less_xy_2, Less_yx_2, Left_turn_2, Less_signed_distance_to_line_2, Less_rotate_ccw_2, Orientation_2;需要的方法有:拷貝構造函數,Equal_2 equal_2_object(), Less_xy_2 less_xy_2_object(), Less_yx_2 less_yx_2_object(), Less_signed_distance_to_line_2 less_signed_distance_to_line_2_object (), Less_rotate_ccw_2 less_rotate_ccw_2_object(), Left_turn_2 left_turn_2_object(), Orientation_2 orientation_2_object()

Model

CGAL::Convex_hull_constructive_traits_2
CGAL::Convex_hull_traits_2<R>
CGAL::Convex_hull_traits_adapter_2<R>
CGAL::Projection_traits_xy_3<K>
CGAL::Projection_traits_yz_3 <K>
CGAL::Projection_traits_xz_3<K>

代碼示例(僅使用kernel)

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/ch_graham_andrew.h>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::Point_2 Point_2;
int main()
{
  CGAL::set_ascii_mode(std::cin);
  CGAL::set_ascii_mode(std::cout);
  std::istream_iterator< Point_2 >  in_start( std::cin );
  std::istream_iterator< Point_2 >  in_end;
  std::ostream_iterator< Point_2 >  out( std::cout, "\n" );
  CGAL::ch_graham_andrew( in_start, in_end, out );
  return 0;
}

ch_graham_andrew提供了兩個接口,如下:

// With out traits
template <class InputIterator, class OutputIterator>
inline
OutputIterator
ch_graham_andrew( InputIterator  first,
                  InputIterator  last,
                  OutputIterator result );
// With traits
template <class InputIterator, class OutputIterator, class Traits>
OutputIterator
ch_graham_andrew( InputIterator  first,
                  InputIterator  last,
                  OutputIterator result,
                  const Traits&  ch_traits );

那么就需要詳細看下Kernel的實現,此處直看偏特化為true的情況:

class Epick
  : public Filtered_kernel_adaptor<
               Type_equality_wrapper< Simple_cartesian<double>::Base<Epick>::Type, Epick >, true >
{};

typedef Epick Exact_predicates_inexact_constructions_kernel;

首先看一下最內層Simple_cartesian<double>::Base<Epick>::Type對應的是:Cartesian_base_no_ref_count<double,Epick>,於是得到

class Epick
  : public Filtered_kernel_adaptor<
               Type_equality_wrapper< Cartesian_base_no_ref_count<double,Epick>, Epick >, true >
{};

Epick最后的基類是:Cartesian_base,該基類定義了Point_2, Vector_2等數據結構,如下 :

typedef PointC2<Kernel>                             Point_2;
typedef VectorC2<Kernel>                            Vector_2;
typedef DirectionC2<Kernel>                         Direction_2;
/// ...

PointC2Vector2DirectionC2等是可以找到具體實現的。其他的概念中對應的類型和函數的實現見:

template < typename FT_, typename Kernel_ >
struct Cartesian_base_no_ref_count
  : public Cartesian_base< Kernel_, FT_ >
{
    typedef FT_                                           RT;
    typedef FT_                                           FT;

    // The mechanism that allows to specify reference-counting or not.
    template < typename T >
    struct Handle { typedef T   type; };

    template < typename Kernel2 >
    struct Base { typedef Cartesian_base_no_ref_count<FT_, Kernel2>  Type; };

    typedef Kernel_ K;
#define CGAL_Kernel_pred(Y,Z) typedef CartesianKernelFunctors::Y<K> Y; \
                              Y Z() const { return Y(); }
#define CGAL_Kernel_cons(Y,Z) CGAL_Kernel_pred(Y,Z)

#include <CGAL/Kernel/interface_macros.h> 
};

#include <CGAL/Kernel/interface_macros.h> 定義了概念中對應的函數,類型,具體如下:

CGAL_Kernel_pred(Less_signed_distance_to_line_2,
                 less_signed_distance_to_line_2_object)
CGAL_Kernel_pred(Equal_2,
		 equal_2_object)
CGAL_Kernel_pred(Less_xy_2,
		 less_xy_2_object)
CGAL_Kernel_pred(Less_yx_2,
		 less_yx_2_object)
CGAL_Kernel_pred(Less_signed_distance_to_line_2,
                 less_signed_distance_to_line_2_object)
CGAL_Kernel_pred(Less_rotate_ccw_2,
		 less_rotate_ccw_2_object)
CGAL_Kernel_pred(Left_turn_2,
                 left_turn_2_object)
CGAL_Kernel_pred_RT(Orientation_2,
		    orientation_2_object)
// ...

上面的定義都可以通過下面的宏進行展開:

#define CGAL_Kernel_pred(Y,Z) typedef CartesianKernelFunctors::Y<K> Y; \
                              Y Z() const { return Y(); }

CartesianKernelFunctors中定義了各種概念上具體的Functor類型,這些Functor提供了一些輔助的幾何關系計算的功能,同時這些類型的實現是由Cartesian_base中對應的類型組裝而成的。

template <class InputIterator, class OutputIterator>
inline
OutputIterator
ch_graham_andrew( InputIterator  first,
                  InputIterator  last,
                  OutputIterator result )
{ 
    typedef std::iterator_traits<InputIterator>   ITraits;
    typedef typename ITraits::value_type          value_type;  // Kernel::Point_2
    typedef CGAL::Kernel_traits<value_type>       KTraits;     // 
    typedef typename KTraits::Kernel              Kernel;      // Kernel
    return ch_graham_andrew( first, last, result, Kernel()); 
}

算法內部中利用了kernel中依據概念提供的數據結構和輔助函數。

代碼示例(使用Traits)

對應的示例代碼如下:

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/convex_hull_2.h>
#include <CGAL/Convex_hull_traits_adapter_2.h>
#include <CGAL/property_map.h>
#include <vector>
#include <numeric>

typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef K::Point_2 Point_2;
typedef CGAL::Convex_hull_traits_adapter_2<K,
          CGAL::Pointer_property_map<Point_2>::type > Convex_hull_traits_2;
int main()
{
  std::vector<Point_2> points = { Point_2(10,0),
                                  Point_2(3,4),
                                  Point_2(0,0),
                                  Point_2(10,10),
                                  Point_2(2,6) };
  
  std::vector<std::size_t> indices(points.size()), out;
  std::iota(indices.begin(), indices.end(),0);
  
  CGAL::convex_hull_2(indices.begin(), indices.end(), std::back_inserter(out),
                      Convex_hull_traits_2(CGAL::make_property_map(points)));
  for( std::size_t i : out){
    std::cout << "points[" << i << "] = " << points[i] << std::endl;
  }
  return 0;
}

先來認識下:CGAL::make_property_map

template <class T>
struct Pointer_property_map{
  typedef boost::iterator_property_map< T*,
                              boost::typed_identity_property_map<std::size_t>,
                              T,
                              T&> type; ///< mutable `LvaluePropertyMap`
  typedef boost::iterator_property_map< const T*,
                              boost::typed_identity_property_map<std::size_t>,
                              T,
                              const T&> const_type; ///< non-mutable `LvaluePropertyMap`
};

template <class T>
inline
typename Pointer_property_map<T>::type
make_property_map(T* pointer)
{
  return typename Pointer_property_map<T>::type(pointer);
}

可見最終會導向:boost::iterator_property_map。簡單來講就是用來將隨機訪問的迭代器轉換成LvaluePropertyMap,該類型提供了operator[],和get()函數來訪問對象。進一步了解可以查找:Boost Property Map Library

接着看一下:Convex_hull_traits_2(CGAL::make_property_map(points))就是構建了一個traits類。這個traits內部直接實現了concepts中給出的需求,如下:

template<class Base_traits,class PointPropertyMap>
class Convex_hull_traits_adapter_2:public Base_traits{
	typedef typename boost::property_traits<PointPropertyMap>::key_type Point_2;
    struct Less_xy_2 : public Base_traits::Less_xy_2{
        Less_xy_2(const PointPropertyMap& ppmap,const typename Base_traits::Less_xy_2& base):
        Base_traits::Less_xy_2(base),ppmap_(ppmap){}
        const PointPropertyMap& ppmap_;
        bool operator()(Arg_type p,Arg_type q) const {
            return static_cast<const typename Base_traits::Less_xy_2*>(this)->operator()(get(ppmap_,p),get(ppmap_,q));
        }
    };

    struct Equal_2 : public Base_traits::Equal_2{ ... }
    //...
    Equal_2 equal_2_object () const {return Equal_2(ppmap_,static_cast<const Gt*>(this)->equal_2_object() );}
    Left_turn_2 left_turn_2_object () const {return Left_turn_2(ppmap_,static_cast<const Gt*>(this)->left_turn_2_object() );}
    //...

這里的Base_traits就是對應上面示例中的的Kernel。以Less_xy_2為例,operator()最后調用的是:

static_cast<const typename Base_traits::Less_xy_2*>(this)->operator()(get(ppmap_,p),get(ppmap_,q));

這里就有完美的切換到了Kernel的實現中到了。

But,為什么么要這么設計,這么實現呢?這個我就不懂了。。,希望大家一起探索下。。

小結

針對相關代碼的閱讀,可以先理解concept中對應的數據類型和函數。然后再看對應的算法實現。看算法實現的時候,就不用過多關注kernel中和concept相關的內容了,更多的關注算法實現的過程即可。不然代碼跳來跳去很快就會暈暈乎乎了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM