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

  1. CGAL::Convex_hull_constructive_traits_2
  2. CGAL::Convex_hull_traits_2<R>
  3. CGAL::Convex_hull_traits_adapter_2<R>
  4. CGAL::Projection_traits_xy_3<K>
  5. CGAL::Projection_traits_yz_3 <K>
  6. CGAL::Projection_traits_xz_3<K>

代码示例(仅使用kernel)

  1. #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
  2. #include <CGAL/ch_graham_andrew.h>
  3. typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
  4. typedef K::Point_2 Point_2;
  5. int main()
  6. {
  7. CGAL::set_ascii_mode(std::cin);
  8. CGAL::set_ascii_mode(std::cout);
  9. std::istream_iterator< Point_2 > in_start( std::cin );
  10. std::istream_iterator< Point_2 > in_end;
  11. std::ostream_iterator< Point_2 > out( std::cout, "\n" );
  12. CGAL::ch_graham_andrew( in_start, in_end, out );
  13. return 0;
  14. }

ch_graham_andrew提供了两个接口,如下:

  1. // With out traits
  2. template <class InputIterator, class OutputIterator>
  3. inline
  4. OutputIterator
  5. ch_graham_andrew( InputIterator first,
  6. InputIterator last,
  7. OutputIterator result );
  8. // With traits
  9. template <class InputIterator, class OutputIterator, class Traits>
  10. OutputIterator
  11. ch_graham_andrew( InputIterator first,
  12. InputIterator last,
  13. OutputIterator result,
  14. const Traits& ch_traits );

那么就需要详细看下Kernel的实现,此处直看偏特化为true的情况:

  1. class Epick
  2. : public Filtered_kernel_adaptor<
  3. Type_equality_wrapper< Simple_cartesian<double>::Base<Epick>::Type, Epick >, true >
  4. {};
  5. typedef Epick Exact_predicates_inexact_constructions_kernel;

首先看一下最内层Simple_cartesian<double>::Base<Epick>::Type对应的是:Cartesian_base_no_ref_count<double,Epick>,于是得到

  1. class Epick
  2. : public Filtered_kernel_adaptor<
  3. Type_equality_wrapper< Cartesian_base_no_ref_count<double,Epick>, Epick >, true >
  4. {};

Epick最后的基类是:Cartesian_base,该基类定义了Point_2, Vector_2等数据结构,如下 :

  1. typedef PointC2<Kernel> Point_2;
  2. typedef VectorC2<Kernel> Vector_2;
  3. typedef DirectionC2<Kernel> Direction_2;
  4. /// ...

PointC2Vector2DirectionC2等是可以找到具体实现的。其他的概念中对应的类型和函数的实现见:

  1. template < typename FT_, typename Kernel_ >
  2. struct Cartesian_base_no_ref_count
  3. : public Cartesian_base< Kernel_, FT_ >
  4. {
  5. typedef FT_ RT;
  6. typedef FT_ FT;
  7. // The mechanism that allows to specify reference-counting or not.
  8. template < typename T >
  9. struct Handle { typedef T type; };
  10. template < typename Kernel2 >
  11. struct Base { typedef Cartesian_base_no_ref_count<FT_, Kernel2> Type; };
  12. typedef Kernel_ K;
  13. #define CGAL_Kernel_pred(Y,Z) typedef CartesianKernelFunctors::Y<K> Y; \
  14. Y Z() const { return Y(); }
  15. #define CGAL_Kernel_cons(Y,Z) CGAL_Kernel_pred(Y,Z)
  16. #include <CGAL/Kernel/interface_macros.h>
  17. };

#include <CGAL/Kernel/interface_macros.h> 定义了概念中对应的函数,类型,具体如下:

  1. CGAL_Kernel_pred(Less_signed_distance_to_line_2,
  2. less_signed_distance_to_line_2_object)
  3. CGAL_Kernel_pred(Equal_2,
  4. equal_2_object)
  5. CGAL_Kernel_pred(Less_xy_2,
  6. less_xy_2_object)
  7. CGAL_Kernel_pred(Less_yx_2,
  8. less_yx_2_object)
  9. CGAL_Kernel_pred(Less_signed_distance_to_line_2,
  10. less_signed_distance_to_line_2_object)
  11. CGAL_Kernel_pred(Less_rotate_ccw_2,
  12. less_rotate_ccw_2_object)
  13. CGAL_Kernel_pred(Left_turn_2,
  14. left_turn_2_object)
  15. CGAL_Kernel_pred_RT(Orientation_2,
  16. orientation_2_object)
  17. // ...

上面的定义都可以通过下面的宏进行展开:

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

CartesianKernelFunctors中定义了各种概念上具体的Functor类型,这些Functor提供了一些辅助的几何关系计算的功能,同时这些类型的实现是由Cartesian_base中对应的类型组装而成的。

  1. template <class InputIterator, class OutputIterator>
  2. inline
  3. OutputIterator
  4. ch_graham_andrew( InputIterator first,
  5. InputIterator last,
  6. OutputIterator result )
  7. {
  8. typedef std::iterator_traits<InputIterator> ITraits;
  9. typedef typename ITraits::value_type value_type; // Kernel::Point_2
  10. typedef CGAL::Kernel_traits<value_type> KTraits; //
  11. typedef typename KTraits::Kernel Kernel; // Kernel
  12. return ch_graham_andrew( first, last, result, Kernel());
  13. }

算法内部中利用了kernel中依据概念提供的数据结构和辅助函数。

代码示例(使用Traits)

对应的示例代码如下:

  1. #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
  2. #include <CGAL/convex_hull_2.h>
  3. #include <CGAL/Convex_hull_traits_adapter_2.h>
  4. #include <CGAL/property_map.h>
  5. #include <vector>
  6. #include <numeric>
  7. typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
  8. typedef K::Point_2 Point_2;
  9. typedef CGAL::Convex_hull_traits_adapter_2<K,
  10. CGAL::Pointer_property_map<Point_2>::type > Convex_hull_traits_2;
  11. int main()
  12. {
  13. std::vector<Point_2> points = { Point_2(10,0),
  14. Point_2(3,4),
  15. Point_2(0,0),
  16. Point_2(10,10),
  17. Point_2(2,6) };
  18. std::vector<std::size_t> indices(points.size()), out;
  19. std::iota(indices.begin(), indices.end(),0);
  20. CGAL::convex_hull_2(indices.begin(), indices.end(), std::back_inserter(out),
  21. Convex_hull_traits_2(CGAL::make_property_map(points)));
  22. for( std::size_t i : out){
  23. std::cout << "points[" << i << "] = " << points[i] << std::endl;
  24. }
  25. return 0;
  26. }

先来认识下:CGAL::make_property_map

  1. template <class T>
  2. struct Pointer_property_map{
  3. typedef boost::iterator_property_map< T*,
  4. boost::typed_identity_property_map<std::size_t>,
  5. T,
  6. T&> type; ///< mutable `LvaluePropertyMap`
  7. typedef boost::iterator_property_map< const T*,
  8. boost::typed_identity_property_map<std::size_t>,
  9. T,
  10. const T&> const_type; ///< non-mutable `LvaluePropertyMap`
  11. };
  12. template <class T>
  13. inline
  14. typename Pointer_property_map<T>::type
  15. make_property_map(T* pointer)
  16. {
  17. return typename Pointer_property_map<T>::type(pointer);
  18. }

可见最终会导向:boost::iterator_property_map。简单来讲就是用来将随机访问的迭代器转换成LvaluePropertyMap,该类型提供了operator[],和get()函数来访问对象。进一步了解可以查找:Boost Property Map Library

接着看一下:Convex_hull_traits_2(CGAL::make_property_map(points))就是构建了一个traits类。这个traits内部直接实现了concepts中给出的需求,如下:

  1. template<class Base_traits,class PointPropertyMap>
  2. class Convex_hull_traits_adapter_2:public Base_traits{
  3. typedef typename boost::property_traits<PointPropertyMap>::key_type Point_2;
  4. struct Less_xy_2 : public Base_traits::Less_xy_2{
  5. Less_xy_2(const PointPropertyMap& ppmap,const typename Base_traits::Less_xy_2& base):
  6. Base_traits::Less_xy_2(base),ppmap_(ppmap){}
  7. const PointPropertyMap& ppmap_;
  8. bool operator()(Arg_type p,Arg_type q) const {
  9. return static_cast<const typename Base_traits::Less_xy_2*>(this)->operator()(get(ppmap_,p),get(ppmap_,q));
  10. }
  11. };
  12. struct Equal_2 : public Base_traits::Equal_2{ ... }
  13. //...
  14. Equal_2 equal_2_object () const {return Equal_2(ppmap_,static_cast<const Gt*>(this)->equal_2_object() );}
  15. Left_turn_2 left_turn_2_object () const {return Left_turn_2(ppmap_,static_cast<const Gt*>(this)->left_turn_2_object() );}
  16. //...

这里的Base_traits就是对应上面示例中的的Kernel。以Less_xy_2为例,operator()最后调用的是:

  1. 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. 【MySQL】剖析MySQL读写分离技术

    主从技术的一个基本流程图: 如何实现主从复制的呢: MySQL  Master(主节点) 1>当一个请求来时,首先由[mysqld]写入到我们的主[data]中 2>然后[mysqld]将 ...

  2. 程序员都在用的 IDEA 插件(不断更新)

    IDEA一些不错的插件分享 目录 IDEA一些不错的插件分享 插件集合 CamelCase Translation LiveEdit MarkDown Navigator Jrebel CheckSt ...

  3. 一言难尽,Jpa这个功能差点让我丢了工作

    故事背景 前阵子,有位朋友在微信上问我数据被删了能不能恢复,我问了下原因,居然是因为一个配置项惹的祸. 故事细节 在 Spring Boot 中使用 jpa 来操作数据库,jpa 就不做详细的介绍了, ...

  4. element 的 Cascader 级联选择器设定默认值

    Cascader 级联选择器 发现在很多的CRM管理系统里面,都有不少页面是用到这种级联选择器的,确实,功能很实用, 不过要设置默认值则应该让不少人头痛,因为你选择的时候 @change 事件的参数就 ...

  5. Nginx 配置文件语法

    一.语法规则: location [=|~|~*|^~] /uri/ { … } = 开头表示精确匹配 ^~ 开头表示uri以某个常规字符串开头,理解为匹配 url路径即可.nginx不对url做编码 ...

  6. 四使用浮动div布局

    刚开始学习的小白,如有不足之处还请各位补充,感激涕零.在html中有两种方式布局<table>表格和<div>,个人剧的使用表格布局可以避免bug产生,并且表格布局相对来说要容 ...

  7. Linux下VCS2014和Verdi2015的联合使用

    VCS和Verdi是IC设计中常用的两款开发工具.VCS是Synopsys公司的产品,和大家所熟知的ModeSim一样的都是EDA仿真工具.Verdi是Nocas公司(已经被Synopsys公司收购) ...

  8. 3.CSS字体属性

    CSS Fonts(字体)属性用定义字体系列,大小粗细,和文字样式(如斜体) 3.1字体系列 CSS使用font-family属性定义文本字体系列 p { font-family:'微软雅黑' ;} ...

  9. rocketmq初识

    概念说明 通常一个消息队列需要掌握的知识点有Topic(主体).Producer(生产者).Consumer(消费者).Queue(队列).Delivery Semantics(消息传递范式) 蛋疼的 ...

  10. RocketMQ安装及入门

    本文是作者原创,版权归作者所有.若要转载,请注明出处. 本文RocketMQ版本为rocketmq-all-4.7.0,系统为win10.请各位去官网下载,也可以留言,我发安装包 RocketMQ安装 ...