2D and 3D Linear Geometry Kernel
CGAL
主要有3个部分组成:
kernal
:它由恒定大小的不可修改几何图元对象和相应操作组成。几何图元对象既被表示为独立类,由表示类参数化,表示类指定用于计算的底层数字类型,几何图元对象也是内核类的成员,这使得内核具有更大的灵活性和适应性。
基本几何数据结构和算法:这些数据结构和算法由traits类参数化,traits类定义数据结构或算法与其使用的几何图元之间的接口。在许多情况下,CGAL
中提供的kernal
类可以用作这些数据结构和算法的traits类。
非几何类的三方库;
这一部分介绍了kernal
。kernal
包含恒定大小的对象,如点、矢量、方向、直线、射线、线段、三角形、等向矩形和四面体。每种类型都有一组可以应用于该类型对象的函数。您通常会找到访问函数(例如点的坐标)、点相对于对象的位置测试、返回边界框的函数、对象的长度或面积等。kernal
还包含一些基本操作,如仿射变换、交点检测和计算 以及距离计算。
1. Kernel Representations
我们的研究对象是d-维仿射欧几里德空间,这里我们主要讨论d=2和d=3的情况。空间中的对象是一组点,表示点的一种常见方法是使用笛卡尔坐标系。我们可以用 ( c 0 , c 1 , . . . , c d − 1 ) (c_0,c_1,...,c_{d-1}) (c0,c1,...,cd−1)表示点的坐标。另一种表示点的方法是齐次坐标系,使用(d+1)维 ( h 0 , h 1 , . . . h d ) (h_0,h_1,...h_d) (h0,h1,...hd)表示点的坐标。通过 c i = h i / h d c_i=h_i/h_d ci=hi/hd可以计算出笛卡尔坐标。注意齐次坐标不是唯一的。对于 λ ≠ 0 \lambda \neq 0 λ=0, ( h 0 , h 1 , . . . h d ) (h_0,h_1,...h_d) (h0,h1,...hd)和 ( λ h 0 , λ h 1 , . . . λ h d ) (\lambda h_0,\lambda h_1,...\lambda h_d) (λh0,λh1,...λhd)表示同一个点。对于 ( c 0 , c 1 , . . . , c d − 1 ) (c_0,c_1,...,c_{d-1}) (c0,c1,...,cd−1)的齐次坐标可以表示为 ( c 0 , c 1 , . . . , c d − 1 , 1 ) (c_0,c_1,...,c_{d-1},1) (c0,c1,...,cd−1,1).我们使用齐次坐标可以避免除法运算,因为附加坐标可以作为公分母。
1.1 Genericity Through Parameterization
几乎所有的内核对象(以及相应的函数)都是带有参数的模板,该参数允许用户选择内核对象的表示形式.对于所有内核对象类型,CGAL::Type<Kernal>
和Kernel::Type
类型都是相同的。
CGAL
为 concept Kernel提供了四类具体模型,两类基于点的笛卡尔表示,两类基于点的齐次表示。kernal
对象的接口被设计成可以同时使用笛卡尔表示和齐次表示。例如,2D中的点也有一个构造函数,它有三个参数(点的三个齐次坐标).
由于稍后将变得明显的原因,Kernel
类为数字类型提供两个typename
,即 Kernel::FT
and Kernel::RT
.类型Kernel::FT
必须满足FieldNumberType
的要求。即Kernel::FT
满足+、-、*、/ 等数学要求。请注意,严格地说,内置类型int不满足FieldNumberType
的要求。因为除法/不是乘法*的逆运算。Kernel::RT
类型的弱一些。此类型必须满足RingNumberType
的要求。仅需满足+,-,*即可。
1.2 Cartesian Kernels
使用 Cartesian<FieldNumberType>
可以选择笛卡尔坐标系。当你选择笛卡尔表示法时,你必须同时声明**坐标的类型**。在笛卡尔坐标系中属于的数字类型必须是FieldNumberType
。如上所述,内置类型int
不是FieldNumberType
。然而,对于一些笛卡尔表示的计算,不需要除法运算,在这种情况下,RingNumberType
就足够了。使用Cartesian<FieldNumberType>
,Cartesian<FieldNumberType>::FT
和Cartesian<FieldNumberType>::RT
都映射到FieldNumberType
。
Cartesian<FieldNumberType>
内部使用引用计数以节省复制成本。CGAL
还提供了 Simple_cartesian<FieldNumberType>
,这是没有引用计数的内核。使用Simple_cartesian<FieldNumberType>
调试更容易,因为坐标存储在类中,因此可以直接访问坐标。根据算法的不同,它的效率也可能略高于或低于 Cartesian<FieldNumberType>
。
1.3 Homogeneous Kernels
齐次坐标可以避免数值计算中的除法运算,因为附加坐标可以作为公分母。避免除法对于精确的几何计算很有用。使用 Homogeneous<RingNumberType>
可以为内核对象的坐标选择齐次表示。由于齐次表示不使用除法,因此与齐次表示类相关联的数字类型必须是RingNumberType
的类型。然而,这个内核提供的一些操作涉及除法,例如计算平方距离或笛卡尔坐标。数字类型 Quotient<RingNumberType>
用来操作除法。这个数字类型可以看作是一个适配器,它将一个RingNumberType
转换为FieldNumberType
。它保持数字为商即a/b。使用 Homogeneous<RingNumberType>
,Homogeneous<RingNumberType>::FT
等价于
Quotient<RingNumberType>
, Homogeneous<RingNumberType>::RT
等价于 RingNumberType
。
Homogeneous<RingNumberType>
内部使用引用计数以节省复制成本。CGAL
还提供了 Simple_homogeneous<RingNumberType>
1.4 Kernel as a Traits Class
CGAL基本库中的算法和数据结构是由traits类参数化的,traits类包含了算法或数据结构操作的对象以及执行这些操作的对象。对于基本库中的大多数算法和数据结构,可以将内核用作traits类。对于一些算法,你甚至不需要指定内核;通过传递给算法的几何对象类型自动检测。在其他一些情况下,算法或数据结构需要的比 kernel concept提供的更多。在这些情况下,内核不能用作traits类。
1.5 Choosing a Kernel and Predefined Kernels
如果计算的可靠性对您至关重要,那么正确的选择可能是保证精确计算的数字类型。 Filtered_kernel
提供了一种应用过滤技术的方法[1],以实现具有精确有效谓词的内核。还有一些人会更喜欢 double,因为它们需要速度,并且可以接受近似结果。
为了方便用户,CGAL
提供了3个普遍使用的内核。
-
它们都是笛卡尔核。
-
他们都支持数值类型为double的笛卡尔坐标系,创建点集。
-
这5个核都提供了精确的几何谓词
-
它们处理几何构造的方式不同:
Exact_predicates_inexact_constructions_kernel
:提供精确的几何谓词,但由于舍入错误,几何构造可能不精确。然而,对于许多CGAL
算法来说,它已经足够了,而且比下面具有精确构造的核更快。Exact_predicates_exact_constructions_kernel
:除了精确的几何谓词外,还提供精确的几何结构。Exact_predicates_exact_constructions_kernel_with_sqrt
:同上,但是数字类型是概念FieldWithSqrt
的模型。Exact_predicates_exact_constructions_kernel_with_kth_root
:同上,但是数字类型是概念FieldWithKthRoot
的模型。Exact_predicates_exact_constructions_kernel_with_root_of
: 同上,但是数字类型是概念FieldWithRootOf
的模型
2. Kernel Geometry
2.1 Points and Vectors
在CGAL
中,我们严格区分点、向量和方向。
点:欧式空间的点;
向量:两个之差,且有方向
方向: 忽略长度,只有方向。
CGAL
定义ORIGN
表示原点,这个常数用于点和向量之间的转换。
Cartesian<double>::Point_2 p(1.0, 1.0), q;
Cartesian<double>::Vector_2 v;
v = p - ORIGIN;
q = ORIGIN + v;
assert( p == q )
除了点(Kernel::Point_2
,Kernel::Point_3
)、向量(Kernel::Vector_2
,Kernel::Vector_3)
和方向(Kernel::Direction_2
,Kernel::Direction_3
),CGAL
还提供直线、射线、线段、平面、三角形、四面体、等长方体、圆和球体。
2.3 Orientation and Relative Position
CGAL中的几何对象具有测试点相对于对象的位置的成员函数。这些对象有一个成员函数的 oriented_side() ,该函数确定测试点是在正的一侧,还是在负的一侧,还是在定向的边界上。这些函数返回一个面向类型的值Oriented_side。
3. Predicates and Constructions
3.1 Predicates
谓词是几何内核的核心。它们是组成几何算法和封装决策的基本单元。因此,它们的正确性对于控制流以及几何算法实现的正确性至关重要。CGAL
在广义上使用谓词这个词。不仅返回布尔值的组件称为谓词,而且返回枚举类型(如比较结果或方向)的组件也被称为谓词。我们称之为组件(components,),因为谓词是作为函数和函数对象(由内核类提供)实现的。
CGAL
为点集的方向提供谓词 (orientation
(), left_turn
(), right_turn
(),collinear
(), coplanar
()),用于按给定的顺序比较点,特别是比较笛卡尔坐标(lexicographically_xy_smaller
()),圆内和球内测试,以及比较距离的谓词。
3.2 Constructions
生成既不是bool
类型也不是enum
类型的对象的函数和函数对象称为构造。构造涉及新数值的计算,并且可能由于舍入误差而不精确,除非使用具有精确数字类型的核。
仿射变换(Kernel::Aff_transformation_2
,Kernel::Aff_transformation_3
)允许在任意仿射变换下生成新的对象.这些变换包括平移、旋转(仅在2D中)和缩放。核中的大多数几何对象都有一个成员函数transform(Aff_transformation t)
.
CGAL
中提供计算距离平方的函数。为什么不提供直接计算距离的函数?第一,这两个值可以相互推导。第二,平方距离可以在更多情况下计算,并且计算成本更低。直接计算距离可能涉及sqrt
操作,这有两个缺点:
- sqrt操作可能代价高昂。
- 有些数字类型没有定义sqrt运算,特别是整数类型和有理数。
3.3 Intersections and Variant Return Types
有些函数,例如intersection()
,可以返回不同类型的对象。为了实现这一点,CGAL
使用类型的返回值 boost::optional<
boost::variant< T... > >T...
是所有可能生成的几何对象的列表.通过元函数可以确定交集的精确结果类型: cpp11::result_of<Kernel::Intersect_2(Type1, Type2)>
,其中Type1
和Type2
是交集计算中使用的对象类型。
在下面的例子中,使用result_of来查询交集计算返回值的类型:
Segment_2 seg_1(Point_2(0, 0), Point_2(2, 2));
Segment_2 seg_2(Point_2(0, 2), Point_2(2, 0));
CGAL::cpp11::result_of<Kernel::Intersect_2(Segment_2, Segment_2)>::
type p = CGAL::intersection(seg_1, seg_2);
std::cout << typeid(p).name() << std::endl;
std::cout << p.value() << std::endl;
Point_2 *p1 = boost::get<Point_2>(&*p);
std::cout << p1->x() << " " << p1->y() << std::endl;
3.4 Constructive Predicates
对于测试点p相对于由三个点q、r和s定义的平面的情况,可能会尝试构造平面Kernel::plane_3(q,r,s)
并使用oriented_side§。但是,除非数字类型是精确的,否则所构造的平面只是近似的,舍入误差可能会导致oriented_side§返回一个与p、q、r和s的实际方向不同的方向。
在CGAL
中,我们提供了谓词,在这些谓词中,这些几何决策直接参考输入点p、q、r、s,而没有像平面这样的中间对象。对于上述测试,建议使用orientation(p,q,r,s)来获得结果。
4. Extensible Kernel
这部分描述用户如何将用户定义的几何类插入到现有的CGAL
内核中。
CGAL
定义了几何核心的概念。这样的内核提供类型、构造对象和通用谓词。在CGAL
的基本库中,计算几何算法和数据结构的大多数实现都是通过一个几何特征类来参数化类或函数的。在大多数情况下,这个几何特征类必须是CGAL几何核心概念的模型(但也有一些例外)。详细参考文献