CGAL代码阅读跳坑指南

整体框架介绍

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相关的内容了,更多的关注算法实现的过程即可。不然代码跳来跳去很快就会晕晕乎乎了。

CGAL代码阅读跳坑指南的更多相关文章

  1. JavaScript 跳坑指南

    JavaScript 跳坑指南 坑0-String replace string的replace方法我们经常用,替换string中的某些字符,语法像这样子 string.replace(subStr/ ...

  2. 两百条微信小程序跳坑指南(不定时更新)

    微信小程序联盟出品 跳坑textarea<二百二十三>不显示文本及textarea相关问题集合跳坑<二百一十三> background-image无法获取本地资源图片....跳 ...

  3. [转帖]Kubernetes - nginx-ingress 配置跳坑指南

    Kubernetes - nginx-ingress 配置跳坑指南 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明. 本文链接:https:// ...

  4. Xamarin安装和跳坑指南

    安装Checklist 注意:本文只描述安装过程,由于组件的版本更新很快,为保证文章时效性,不提供下载链接,也尽可能不指明具体版本. 安装Visual Studio 2015进行默认安装,除非已经FQ ...

  5. 分布式监控系统Zabbix3.2跳坑指南

    zabbix是什么在此就不多作介绍了,可以参考之前的文章 零代码如何打造自己的实时监控预警系统 ,这篇主要介绍安装及注意事项. 主要分为服务端和客户端安装,客户端又分为Linux.Windows. 服 ...

  6. 【H5】316- 移动端H5跳坑指南

    最近在一个移动端的 Web 项目中踩了很多的坑,感觉有必要把它们记录下来,分享给即将踏入移动端 Web 开发大门的朋友们,更好的解决ios和android兼容. 1.input获取焦点时,页面被放大 ...

  7. python3-django+uwsgi+supervisor+nginx跳坑指南(记录)

    首先运行django项目:在项目目录内: python manage.py runserver 0.0.0.0:8000 外部服务器访问:http://www.xxx.com:8000/ 可以正常运行 ...

  8. 原创:跳坑指南——微信小程序真机预览跟本地不同的问题

    微信小程序中出现最多的一个问题,就是真机跟本地不同:我简单列举一些我发现的原因,给大家参考,大家也可以把自己发现的东西回复给我,给我参考:本地看不到数据,就先让本地能看到数据,再看本帖.... 1:本 ...

  9. PCB走线角度选择 — PCB Layout 跳坑指南

    现在但凡打开SoC原厂的PCB Layout Guide,都会提及到高速信号的走线的拐角角度问题,都会说高速信号不要以直角走线,要以45度角走线,并且会说走圆弧会比45度拐角更好.狮屎是不是这样?PC ...

随机推荐

  1. xctf-misc 新手区 wp

    目录 this_is_flag pdf SimpalRAR ext3 stegano this_is_flag Most flags are in the form flag{xxx}, for ex ...

  2. NetAnalyzer笔记 之 十二 NetAnalyzer 6.0 的使用方法 -- 1.初识NetAnalyzer

    上次写NetAnalyzer使用方法是2016年的时候了,在后来NetAnalyzer经过了巨大的版本更变,但是因为个人原因,一直未对使用方法进行更新,现在NetAnalyzer最新的6.0已经发布了 ...

  3. UVALive3720

    题目大意:见刘汝佳<算法竞赛入门经典——训练指南>P173. 解题思路: 问题可以转化为求共有多少条过点阵中的点的斜线.其中必定包含左斜线和右斜线,由于点阵式对称的,所以我们只需求出左右斜 ...

  4. 三、HTML元素

    嵌套的HTML元素 <!--以下实例包含了三个HTML元素,分别是<html>.<body>.<p>--> <!DOCTYPE html> ...

  5. RESTful api 功能测试

    0 为什么要写测试代码 代码写好了,如果能点或者能看,开发人员一般会自己点点或看看,如果没有发现问题就提交测试:更进一步,代码写好后,运行测试代码,通过后提交测试.将流程抽象下: 功能1编码-> ...

  6. [COCOS2DX-LUA]0-005.cocos2dx中关于全面屏和折叠屏的适配的一些见解

    1.随着科技的发展,我们可以看到从iphoneX的刘海屏开始,引发了各种全面屏和异形屏的出现.这是科技的进步,但是对于各大的应用厂商来说,苦不堪言. 2.当然 ,吐槽归吐槽,我们还是要理智的去对待这个 ...

  7. [Python基础]004.语法(3)

    语法(3) 方法 定义 调用 参数 返回 模块 引入模块 写模块 模块名称 dir() 方法 定义 语法 def 方法名(参数): 返回值 return 没有指定返回值的方法,默认返回空值 None ...

  8. [注]一条牛B的游戏推送要具备哪些条件?

    旁白:推送内容写的好,可以给游戏带来很大的收益,但如果写的很糟糕,就可能是在提醒用户还有一个该卸载的软件没卸载.那么如何写出一个优秀的推送内容呢? 总结:推送文字八字原则 从运营的角度来讲,我们需要找 ...

  9. 此flash player与您的地区不相容——更换新版本edge后出现的问题

    最新切换到了edge浏览器,使用flash时提示:"此flash player与您的地区不相容",而chrome是没有问题的.网上找到解决方案,发现一个可以有效解决的方式,如下: ...

  10. 【Python】组合数据类型

    集合类型 集合类型定义 集合是多个元素的无序组合 集合类型与数学中的集合概念一致 集合元素之间无序,每个元素唯一,不存在相同元素 集合元素不可更改,不能是可变数据类型 理解:因为集合类型不重复,所以不 ...