CGAL入门解释

关于CGAL其实我是不想学的,因为太小众了,根本没有多少中国人学,网上的中文资料奇少,从这就知道根本就没有哪个中国公司会用到。我身边也没有人会,即便是做游戏的同学,估计只有外国的科研人员在用。

 

参考资料其实就只有官网

https://doc.cgal.org/latest/Manual/index.html

 

然后就没有任何的书籍和博客可以参考了。但是官网的文档其实看起来相当简单,代码的注释也很少,但是提到的内容都是比较高深的,完全就是挑战读者的学识,反正一开始我是看两眼就没看下去了。

 

不过最近看到国外的开发者又在推荐用CGAL写算法,而且还写了一个工具,不会CGAL确实是看不懂,因此没办法只能硬着头皮看下去了。

 

入门必须从第一步看起

https://doc.cgal.org/latest/Manual/tutorial_hello_world.html

 

CGAL的难点其实不在于数学和编程,而是它会引入一些数学和编程都很少看到的名词。

 

首先第一个叫kernel,这在编程或计算机领域中称为内核,然而CGAL中的这个kernel看起来像解析几何中坐标系的意思多点,而跟计算机的理论不是有太大的关联。

 

第二个叫predicates,在编程或算法领域中称为谓词,这个就更难懂了。不过在CGAL中我觉得这个跟算法中的谓词关系也不大。我更倾向于翻译为断言,predicates应该是指一类函数,这一类函数的作用通常是用来判断一些几何关系的,因此返回值是一个离散集,编程中通常会用到枚举类来表示这些离散结果集。

 

第三个叫constructions,这个术语在数学中听得多一点,称为构造,但是在几何领域中也不多听得到,所以还是CGAL的自创名词。constructions应该还是指一类函数,用来返回数字或者是几何元素。

 

例子Kernel_23/points_and_segment.cpp

 

解释了kernel、predicates和constructions的用法

首先kernel使用了双精度浮点型的简单笛卡尔坐标系内核

typedef CGAL::Simple_cartesian<double> Kernel

 

typedef Kernel::Point_2 Point_2; 是指二维点

typedef Kernel::Segment_2 Segment_2; 是指二维有向线段

 

然后使用constructions函数CGAL::squared_distance计算两点之间的、及有向线段和点之间的平方距离

再使用predicates函数CGAL::orientation计算点和向量之间的几何关系,还自创了一些返回值名词:共线collinear、偏左left turn和偏右right turn

 

例子Kernel_23/surprising.cpp

 

这里解释了,由于计算机计算的舍入误差,会导致predicates函数的返回结果并不准确,使得当以小数作为参数的时候,返回值明明应该是共线的却返回不共线。

 

这里,我试着添加一句

std::cout << typeid(q.y()).name() << std::endl;

然后输出double,没错

然而文档里却写这些小数不代表双精度浮点型,我就觉得很费解,是不是写错了?应该是说的全精度full precision吧,因为全精度只有数学才会有,而计算机是不可能实现的。

 

例子 Kernel_23/exact.cpp

 

注意到这里用了一个新的kernel

typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel

表示这是一个predicates和constructions都精确的内核。

然而输出的结果显示第一个还是不共线,于是文档解释道还是浮点型运算的问题。注意,浮点型并不仅仅指的float类型,double类型也是浮点型,只不过是双精度而已。

 

然而第二个怎么就能解析为全精度的呢,我也不是很理解,大家都是小数,凭什么用的字符串流输入的就能判断为共线呢?真的坑。

 

在这一个例子中,我又添加同样一句

std::cout << typeid(q.y()).name() << std::endl;

然后输出

class CGAL::Lazy_exact_nt<class CGAL::Gmpq>

可见,用了新的kernel之后,封装坐标数值的类型不再是double了,而是一个gmp类对象,从而使得运算的精度不同。

 

然后教程又说了,其实在大多数情况下都不需要用到全精度的构造constructions,而且也很耗开销,于是就引入了下一个例子。

 

例子 Convex_hull_2/array_convex_hull_2.cpp

 

这个例子用到了新的kernel

typedef CGAL::Exact_predicates_inexact_constructions_kernel K;

 

表示精确的predicates和不精确的constructions

 

然后就计算点集的凸包和处于凸包上的点

 

注意到这一句

Point_2 *ptr = CGAL::convex_hull_2( points, points+5, result );

连注释都没有,如果没怎么接触C语言的恐怕看起来都费劲

 

CGAL::convex_hull_2的参数依次代表

点数组的起始位置,点数组的末端位置,和结果数组

 

然而结果数组也是一个5元的点数组,那怎么知道哪些点在凸包的边缘上呢?

原来,这个结果数组是经过排序的,会分成两部分,前端部分会存储处于凸包内部的点,而后端部分会存储处于凸包边缘的点,于是返回一个边界指针,用来存储前后两部分之间的边界的位置。

 

例子 Convex_hull_2/vector_convex_hull_2.cpp

 

由于用数组的方法有点过于偏底层计算机方面的知识,这会使得偏数学的用户感觉很不舒服,于是就引入了容器类vector来存储点集和结果,这就使得函数直接就返回凸包边缘上的点,不需要再分界了。

 

例子 Convex_hull_2/convex_hull_yz.cpp

 

这个例子引入了一个称为Traits的类型,我把它翻译为特质。当然,这又是CGAL自创的名词,所以即使翻译了也不能有什么理解上的帮助。

 

看文档的解释,Traits的类通常是作为算法的参数,因为一些算法会有很多种模式,或许是用于排序或者其它用途,Traits就是用于确定使用哪一种模式。

 

这个例子有一点复杂,使用了核套核

typedef CGAL::Projection_traits_yz_3<K3> K;

typedef K::Point_2 Point_2;

 

虽然写的二维点,但是内核K是一个三维核,所以不看文档我都不知道这个程序什么意思。

CGAL::convex_hull_2( input_begin, input_end, output, K() );

 

原来是让你输入一些三维点,并且以回车作为分隔

 

我当时测这个程序的时候,不停地输入数据,然而不知道怎么结束输入,CGAL文档也没写,真的很尴尬。

 

后来发现std::istream_iterator是用的C++的标准库,所以原来这不是CGAL的锅,不过CGAL说明一下也行啊,注释也不写,这让没用过这个类的程序员情何以堪啊?

 

后来看到这篇博客

https://www.cnblogs.com/VIPler/p/4367308.html

才发现原来是Ctrl+z结束,不过其实我输入一些非数字字符也能结束。但是这只不过是因为这里只接收数字数据,而std::istream_iterator其实并不限于接收数字,还能接收字符串的,因此Ctrl+z终究还是唯一的结束方法。

 

最后,CGAL又提出了新的术语:概念Concepts和模型Models

可以说,CGAL又在自创名词了,其实这就是面向对象中经常说的老话题:模板类或者泛型

Concept就是有一系列共同运算体系的其中一个泛型,而Model就是这个泛型中的其中一个实现类

 

其实就是很熟悉的概念,愣是被CGAL弄得是另一个陌生的东西我看到都于心不忍。

 

接下来就是一个一个模块地看了

https://doc.cgal.org/latest/Manual/packages.html

 

不过老实说,模块还是有很多,不过每一个例子其实都比较简短,至于都看完需要多长时间,就需要个人修行了。

发布了12 篇原创文章 · 获赞 17 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/outtt/article/details/104751567