1 Introduction
This part of the reference manual covers the higher-dimensional kernel. The kernel contains objects of constant size, such as point, vector, direction, line, ray, segment, circle. With each type comes a set of functions which can be applied to an object of this type. You will typically find access functions (e.g. to the coordinates of a point), tests of the position of a point relative to the object, a function returning the bounding box, the length, or the area of an object, and so on. The CGAL kernel further contains basic operations such as affine transformations, detection and computation of intersections, and distance computations. Note that this section partly recapitulates facts already mentioned for the lower-dimensional kernel.
本部分覆盖高维内核(higher-dimensional kernel.)。这个内核中包含固定维的对象,如点,向量,方向,直线,射线,线段,圆等。每个类型都有一个函数集用于操作该类型的对象。典型的操作有:存取函数(即点的坐标存取),测试 一个点相对于对象的位置,返回边界盒(bounding box)的函数,长度或面积函数等。CGAL内核还包含基本的操作,如:仿射变换(affine transformations),检测和计算交集,以及距离计算。本节重述(recapitulates )了前面低维内核中的事实。
1.1 Robustness
The correctness proof of nearly all geometric algorithms presented in theory papers assumes exact computation with real numbers. This leads to a fundamental problem with the implementation of geometric algorithms. Naively, often the exact real arithmetic is replaced by inexact floating-point arithmetic in the implementation. This often leads to acceptable results for many input data. However, even for the implementation of the simplest geometric algorithms this simplification occasionally does not work. Rounding errors introduced by inaccurate arithmetic may lead to inconsistent decisions, causing unexpected failures for some correct input data. There are many approaches to this problem, one of them is to compute exactly (compute so accurate that all decisions made by the algorithm are exact) which is possible in many cases but more expensive than standard floating-point arithmetic. C. M. Hoffmann [2], [1] illustrates some of the problems arising in the implementation of geometric algorithms and discusses some approaches to solve them. A more recent overview is given in [3]. The exact computation paradigm is discussed by Yap and Dubé [4] and Yap [5].
In CGAL you can choose the underlying number types and arithmetic. You can use different types of arithmetic simultaneously and the choice can be easily changed, e.g. for testing. So you can choose between implementations with fast but occasionally inexact arithmetic and implementations guaranteeing exact computation and exact results. Of course you have to pay for the exactness in terms of execution time and storage space. See the dedicated chapter for more details on number types and their capabilities and performance.
几何算法理论论文的正确性假定实数的精确计算(exact computation)这一前提。这导致了几何算法实现的基本问题。实现中非精确算法的浮点运算经常取代了精确浮点运算。这经常导致输入数据的可接收性结果(This often leads to acceptable results for many input data)。但是,即使是最简单的几何算法实现,这种方式仍然可能出问题。舍入误差(Rounding errors)的引入会导致不一致的决策,引起正确的输入导致错误的结果。有很多解决这些问题的方法,其中之一是通过精确计算(即算法的精确性保证所有的结果的正确性),这种方法比标准的浮点算法昂贵得多。C. M. Hoffmann 举出了几何算法实现中引起的问题,讨论了解决的一些方法。文献【5】给出了最新的分析。精确计算范例由 Yap 和 Dubé [6] 和 Yap [7].进行了讨论。
CGAL可以选择底层的数据类型和算法。你可以同时使用不同的算术类型并容易改变选择(即通过测试)。所以你可以在运算速度和运算精度之间进行取舍。当然,你需要付出计算时间和存储空间的代价。请参考关于数据类型和它们的能力和性能之间如对此详细分析。
1.2 Genericity
To increase generic usage of objects and predicates the higher-dimensional kernel makes heavy use of iterator ranges as defined in the STL for modeling tuples. Iterators conceptualize C++ pointers.
For an iterator range [first,last)
we define T = tuple [first,last)
as the ordered tuple (T[0],T[1],…T[d−1]) where S[i]=∗++(i)first (the element obtained by i times forwarding the iterator by operator ++
and then dereferencing it to get the value to which it points). We write d = size [first,last)
and S = set [first,last)
to denote the unordered set of elements of the corresponding tuple.
This extends the syntax of random access iterators to input iterators. If we index the tuple as above then we require that ++(d)first=last.
为增加对象(objects)和判定(predicates)的总的使用,高维内核大量使用STL定义的iterator来为元组(tuples)建模。iterator概念化了C++的指针。
对于一个iterator的范围为[first,last),我们定义 T = tuple [first,last)
是一个有序的tuple ((T[0],T[1],…T[d−1]),这里S[i]=∗++(i) (元素由iterator前进 i 次得到,然后通过引用它得到它指向的值)。我们定义d = size [first,last)
并且 S = set [first,last)来指定相应tuple中元素的非有序集合。
这将把随机存取iterator 的语法向输入iterator扩展。如果我们检索这个tuple我们需要++(d)first=last。
2 Kernel Representations
Our object of study is the d-dimensional affine Euclidean space, where d is a parameter of our geometry. Objects in that space are sets of points. A common way to represent the points is the use of Cartesian coordinates, which assumes a reference frame (an origin and d orthogonal axes). In that framework, a point is represented by a d-tuple (c0,c1,…,cd−1), and so are vectors in the underlying linear space. Each point is represented uniquely by such Cartesian coordinates.
Another way to represent points is by homogeneous coordinates. In that framework, a point is represented by a (d+1)-tuple (h0,h1,…,hd). Via the formulae ci=hi/hd, the corresponding point with Cartesian coordinates (c0,c1,…,cd−1) can be computed. Note that homogeneous coordinates are not unique. For λ≠0, the tuples (h0,h1,…,hd) and (λ⋅h0,λ⋅h1,…,λ⋅hd) represent the same point. For a point with Cartesian coordinates (c0,c1,…,cd−1) a possible homogeneous representation is (c0,c1,…,cd−1,1). Homogeneous coordinates in fact allow to represent objects in a more general space, the projective space Pd. In CGAL, we do not compute in projective geometry. Rather, we use homogeneous coordinates to avoid division operations, since the additional coordinate can serve as a common denominator.
我们研究的对象是d维仿射欧几里德空间(affine Euclidean space)。空间内的对象是点的集合。表示点的一个通用的方法是笛卡尔坐标,它是一个参考框架(which reference frame)(一个原点和d维正交轴)。在这个框架下,一个点由d 维元组(c0,c1,…,cd−1)表示,在底层线性空间中是向量。每个点被这个笛卡尔坐标唯一表示。
另一种表示方法是齐次坐标(homogeneous coordinates),其对一个点的表达不唯一(一个点对应多个坐标)。这个框架中一个点由(d+1)唯元组 (h0,h1,…,hd)表示,通过公式ci = hi / hd 可以得到笛卡尔坐标(c0,c1,…,cd−1)。注意,齐次坐标不是唯一的,对于 λ≠0,(h0,h1,…,hd)和((λh0,λh1,…,λhd)表示同一个点。笛卡尔坐标(c0,c1,…,cd−1)可以用(c0,c1,…,cd−1,1)来表示。齐次坐标的更加广义的空间,使用它我们可以避免使用投影计算,即不需要进行除法运算,因为增加的一维可以作为分母。
2.1 Genericity through Parameterization
Almost all the kernel objects (and the corresponding functions) are templates with a parameter that allows the user to choose the representation of the kernel objects. A type that is used as an argument for this parameter must fulfill certain requirements on syntax and semantics. The list of requirements defines an abstract kernel concept. In CGAL such a kernel concept is often also called a representation class and denoted by R
. A representation class provides the actual implementations of the kernel objects. For all kernel objects Kernel_object
of a representation class R
based on Cartesian_d
or Homogeneous_d
, the types CGAL::Kernel_object<R>
and R::Kernel_object
are identical.
CGAL offers three families of concrete models for the concept representation class, two based on the Cartesian representation of points and one based on the homogeneous representation of points. The interface of the kernel objects is designed such that it works well with both Cartesian and homogeneous representation, for example, points have a constructor with a range of coordinates plus a common denominator (the d+1homogeneous coordinates of the point). The common interfaces parameterized with a representation class allow one to develop code independent of the chosen representation. We said "families" of models, because both families are parameterized too. A user can choose the number type used to represent the coordinates and the linear algebra module used to calculate the result of predicates and constructions.
For reasons that will become evident later, a representation class provides two typenames for number types, namely R::FT
and R::RT
. The type R::FT
must fulfill the requirements on what is called a field type in CGAL. This roughly means that R::FT
is a type for which operations +, −, ∗ and / are defined with semantics (approximately) corresponding to those of a field in a mathematical sense. Note that, strictly speaking, the built-in type int
does not fulfill the requirements on a field type, since int
s correspond to elements of a ring rather than a field, especially operation / is not the inverse of ∗. The requirements on the type R::RT
are weaker. This type must fulfill the requirements on what is called a Euclidean ring type in CGAL. This roughly means that R::RT
is a type for which operations +, −, ∗ are defined with semantics (approximately) corresponding to those of a ring in a mathematical sense. A very limited division operation / must be available as well. It must work for exact (i.e., no remainder) integer divisions only. Furthermore, both number types should fulfill CGAL's requirements on a number type.
几乎所有的核心对象和相应的函数都是模板,它带有一个模板参数允许用户选择核心对象的表示方式,该参数使用的类型必须满足一定语法和语义要求。一系列这种要求就定义了一个抽象内核概念。在CGAL中这样的内核也被称为“表达类”(representation class)由R表示。一个“表达类”提供了这个内核对象的所有实现。对于表达类的所有内核对象(Kernel_object
),CGAL::Type<Kernel>和 Kernel::Type
是相同的。
CGAL提供了3个关于 Kernel,的具体模型, 2个基于笛卡尔点集表示而1个基于齐次点集表示。核心对象的接口被设计为同时支持笛卡尔和齐次表达。如:点的构造器带有一个坐标序列和一个公共分母(d+1维的齐次坐标)。用一个核心类参数化的公共接口允许我们独立于选定的表达开发代码(The common interfaces parameterized with a representation class allow one to develop code independent of the chosen representation.)。我们说模型“家族”,原因是两个系列都是参数化的。用户可以选择一种数数类型用来表示坐标。
一个“表达类”为数字类型提供2个类型(typenames ) ,分别是Kernel::FT
和 Kernel::RT。类型
Kernel::FT必须满足CGAL中FieldNumberType概念,这大致意味着Kernel::FT是一种定义了+,−,∗和/的类型,其数学语义(大致)与所关联的field的相关运算相同。需要注意的是:内置类型int不满足field类型的要求,因为int对应的是环(ring)而非域(field),特别/运算不是*运算的逆。Kernel::RT的类型要求则相对较弱,它必须满足CGAL中的RingNumberType概念,这大致意味着Kernel::RT是一种定义了+,−,∗的类型,其数学语义(大致)与所关联的ring的相关运算相同。
2.2 Cartesian Kernel
With Cartesian_d<FieldNumberType,LinearAlgebra>
you can choose Cartesian representation of coordinates. The type LinearAlgebra
must me a linear algebra module working on numbers of type FieldNumberType
. The second parameter defaults to module delivered with the kernel so for short a user can just write Cartesian_d<FieldNumberType>
when not providing her own linear algebra.
When you choose Cartesian representation you have to declare at least the type of the coordinates. A number type used with the Cartesian_d
representation class should be a field type as described above. Both Cartesian_d<FieldNumberType>::FT
and Cartesian_d<FieldNumberType>::RT
are mapped to number type FieldNumberType
. Cartesian_d<FieldNumberType,LinearAlgebra>::LA
is mapped to the type LinearAlgebra
. Cartesian_d<FieldNumberType>
uses reference counting internally to save copying costs.
使用 Cartesian<FieldNumberType,LinearAlgebra>类
,你可以选择坐标的笛卡尔表示方式( Cartesian representation)。类型必须是一个工作于 FieldNumberType
上的线性代数模块(module ),如果用户不提供自己的线性代数,则可写成Cartesian_d<FieldNumberType>
,将表示使用缺省的模块。
当你选择笛卡尔表示方式( Cartesian representation)你需要同时声明坐标的类型。与笛卡尔 Cartesian_d
表达类共用的数字类型应当是一个域数字类型( FieldNumberType )。两个类Cartesian<FieldNumberType>::FT (域trait)和 Cartesian<FieldNumberType>::RT (环trait)都映射到域类型( FieldNumberType
.)。Cartesian_d<FieldNumberType,LinearAlgebra>::LA映射LinearAlgebra类型。
Cartesian<FieldNumberType>
使用内部引用 记数来节省拷贝开销。CGAL也提供了 Simple_cartesian<FieldNumberType>
,它是一个使用笛卡尔表达的核心(kernel )但没有引用记数。使用 Simple_cartesian<FieldNumberType>
时,调试会较为容易,因为坐标是直接保存在类中可以直接存取。依赖这些算法,它会比Cartesian<FieldNumberType>类多多少少效率高一些。另外,在 Simple_cartesian<FieldNumberType>中,
两个类Simple_cartesian<FieldNumberType>::FT (域trait)和 Simple_cartesian<FieldNumberType>::RT(环trait) 都映射到域类型( FieldNumberType
.)。 Cartesian_d<FieldNumberType>
使用内部引用计数来节省拷贝开销。
2.3 Homogeneous Kernel
As we mentioned before, homogeneous coordinates permit to avoid division operations in numerical computations, since the additional coordinate can serve as a common denominator. Avoiding divisions can be useful for exact geometric computation. With Homogeneous_d<RingNumberType,LinearAlgebra>
you can choose homogeneous representation of coordinates with the kernel objects. As for Cartesian representation you have to declare at the same time the type used to store the homogeneous coordinates. Since the homogeneous representation allows one to avoid the divisions, the number type associated with a homogeneous representation class must be a model for the weaker concept Euclidean ring type only.
The type LinearAlgebra
must me a linear algebra module working on numbers of type RingNumberType
. Again the second parameter defaults to module delivered with the kernel so for short one can just write Homogeneous_d<RingNumberType>
when replacing the default is no issue.
However, some operations provided by this kernel involve division operations, for example computing squared distances or returning a Cartesian coordinate. To keep the requirements on the number type parameter of Homogeneous
low, the number type Quotient<RingNumberType>
is used instead. This number type turns a ring type into a field type. It maintains numbers as quotients, i.e. a numerator and a denominator. Thereby, divisions are circumvented. With Homogeneous_d<RingNumberType>
, Homogeneous_d<RingNumberType>::FT
is equal to Quotient<RingNumberType>
while Homogeneous_d<RingNumberType>::RT
is equal to RingNumberType
. Homogeneous_d<RingNumberType,LinearAlgebra>::LA
is mapped to the type LinearAlgebra
.
齐次坐标允许在数的计算中避开除法,因为附加的坐标项可以作为公共的分母。避开除法对于几何精确计算十分有用。使用 Homogeneous_d<RingNumberType,LinearAlgebra>
,你可以为内核对象(kernel objects)选择一种齐次表达。对于笛卡尔坐标表示,我们需要同时声明一个用于保存齐次坐标的类型。因为齐次表示不需要用除法,所以使用的数类型必须是 Euclidean ring的弱类型概念的模型。但这个核心有些操作用到了除法,如计算平方距离(squared distances)或笛卡尔坐标时。为了保持齐次的参数数字类型的低要求( To keep the requirements on the number type parameter of Homogeneous
low),类型Quotient<RingNumberType>被用于需要除法的操作。这个数类型可以认为是一个由 RingNumberType
向 FieldNumberType
转换的适配器。它把数据维护为quotients形式,即一个分子和一个分母。在Homogeneous<RingNumberType>中,Homogeneous<RingNumberType>::FT 等于Quotient<RingNumberType>,同时Homogeneous<RingNumberType>::RT等于 RingNumberType
。LinearAlgebra>::LA
映射LinearAlgebra类型。
2.4 Epick_d Kernel
The kernel Epick_d<DimensionTag>
, short for Exact Predicates Inexact Constructions Kernel is a kernel particularly useful when the dimension of the space is known at compile-time; The template parameter DimensionTag
is then Dimension_tag<d>
where d
is an integer representing the dimension. It may also be used with parameter Dynamic_dimension_tag
, in which case the dimension does not need to be known at compile-time. It uses a Cartesian representation and supports construction of points from double
coordinates. It provides exact geometric predicates, but the geometric constructions are not guaranteed to be exact.
Note that it provides few interfaces in addition to those documented in the Kernel_d
concept. In particular, the type of a point is only available as Epick_d<DimensionTag>::Point_d
, not Point_d<Epick_d<DimensionTag>>
.
Epick_d<DimensionTag>
内核(Exact Predicates Inexact Constructions Kernel的缩写)是当编译时空间维数已知时非常有用的内核。模板参数DimensionTag
表示为Dimension_tag<d>,其中d是表示维度的整数。它编译时空间维度不必须知道时,它可能会和Dynamic_dimension_tag一起用。它使用笛卡尔表示并支持由double构造。它提供了精确的几何判定,但几何构造则不能保证精确。
注意,它提供了Kernel_d
概念文档之外的一些接口,特别是Epick_d<DimensionTag>::Point_d,点的类型只有使用,而不是Point_d<Epick_d<DimensionTag>>。
2.5 Naming Conventions
The use of representation classes does not only avoid problems, it also makes all CGAL classes very uniform. Like Cartesian_d<double>::Point_d
, they always consist of:
使用kernel类不仅避免问题,也使用所有CGAL类非常统一。它们总是由下列组成:
-
The capitalized base name of the geometric object, such as
Point
,Segment
, orTriangle
.大写的几何对象的基名字,如
Point
,Segment
, 或Triangle
. -
An underscore followed by the dimension of the object, for example _2, _3, or _d.
引导维数的下划线,如_2, _3, 或 _d。
- A representation class, which itself may be parameterized with a number type, such as
Cartesian_d<double>
orHomogeneous_d<leda_integer>
, where the type can be found, except forEpick_d<DimensionTag>
where the number type is implicitlydouble
.
一个可能由数的类型参数化的表达类( representation class),如:Cartesian_d<double>
或 Homogeneous_d<leda_integer>
,它的类型是可以找到的,除了 Epick_d<DimensionTag>
,它的数的类型隐含为double。
2.6 Kernel as a Traits Class
Algorithms and data structures in the basic library of CGAL are parameterized by a traits class that subsumes the objects on which the algorithm or data structure operates as well as the operations to do so. For most of the algorithms and data structures in the basic library you can use a kernel as a traits class. For some algorithms you even do not have to specify the kernel; it is detected automatically using the types of the geometric objects passed to the algorithm. In some other cases, the algorithms or data structures need more than is provided by a kernel. In these cases, a kernel can not be used as a traits class.
CGAL的基本库中的算法和数据结构由一个traits类参数化(parameterized),这个traits包含了这些算法或数据结构操作的对象及这样做的操作。对于大多数基本库的的算法和数据结构,你能够将kernel作为一个traits类。对于 一些算法你甚至不需要指定kenel;它被使用传入算法的几何对象类型自动识别。其他情况下,算法或数据结构需要的东西比kernel概念提供的东西要多,所以这种情况下一个kernel不能用作traits类。
2.7 Choosing a Kernel
If you start with integral Cartesian coordinates, many geometric computations will involve integral numerical values only. Especially, this is true for geometric computations that evaluate only predicates, which are tantamount to determinant computations. Examples are triangulation of point sets and convex hull computation.
The dimension d of our affine space determines the dimension of the matrix computations in the mathematical evaluation of predicates. As rounding errors accumulate fast the homogeneous representation used with multi-precision integers is the kernel of choice for well-behaved algorithms. Note, that unless you use an arbitrary precision integer type, incorrect results might arise due to overflow.
If new points are to be constructed, for example the intersection point of two lines, computation of Cartesian coordinates usually involves divisions, so you need to use a field type with Cartesian representation or have to switch to homogeneous representation. double
is a possible, but imprecise field type. You can also put any ring type into Quotient
to get a field type and put it into Cartesian_d
, but you better put the ring type into Homogeneous
. leda_rational
and leda_real
are valid field types, too.
Still other people will prefer the built-in type double
, because they need speed and can live with approximate results, or even algorithms that, from time to time, crash or compute incorrect results due to accumulated rounding errors.
The Epick_d
kernel provides a compromise using double
coordinates. It evaluates predicates exactly, which is slower than plain double
computations, but still faster than using an exact number type thanks to filtering techniques. Constructions are inexact, computed with double
.
如果初始的笛卡尔坐标是整数(integral ),很多几何计算只涉及整数值。特别地,对于只计算判定(predicate)的几何计算是这样的,这与行列式(determinant )计算是相当的。例如:三角形点集和凸包等的计算。
在我们仿射空间中,维度d决定判定的矩阵运算的维度。由于舍入误差快速地积累,使用多精度整数齐次表示是行为良好的算法的选择。注意,除非你使用了任意精度的正数类型,由溢出导致的错误仍可能会发生。
如果新的点需要创建,如两条直线的交点,笛卡尔坐标通常涉及除法。所以,需要在笛卡尔表示中使用FieldNumberType或转换为齐次表达。double类型是一个(不精确的) FieldNumberType
模型。你也可以将任何 RingNumberType
类型经由 Quotient
适配器来得到一个可以用于Cartesian的类型。但是使用基于 RingNumberType
的齐次表达通常是更好的选项。其他的 RingNumberType
包括leda_rational和leda_real。
仍然有人喜欢使用内置的double类型,因为计算速度并且可以允许不精确的数据,即使算法通过一次次的崩溃或积累舍入误差导致的不准确。
Epick_d
kernel内核提供了一个使用double的折衷方案,它精确地计算判定,比普通的double要慢,但由于使用了过滤技术( filtering techniques)它比使用精确数字类型要快。构造则是不精确的,使用double进行计算。
2.8 Inclusion of Header Files
You need just to include a representation class to obtain the geometric objects of the kernel that you would like to use with the representation class, i.e., CGAL/Cartesian_d.h
or CGAL/Homogeneous_d.h
头文件是: CGAL/Cartesian_d.h
or CGAL/Homogeneous_d.h
3 Kernel Geometry
3.1 Points and Vectors
In CGAL, we strictly distinguish between points, vectors and directions. A point is a point in the Euclidean space Ed, a vector is the difference of two points p2, p1 and denotes the direction and the distance from p1 to p2 in the vector space Rd, and a direction is a vector where we forget about its length. They are different mathematical concepts. For example, they behave different under affine transformations and an addition of two points is meaningless in affine geometry. By putting them in different classes we not only get cleaner code, but also type checking by the compiler which avoids ambiguous expressions. Hence, it pays twice to make this distinction.
CGAL defines a symbolic constant ORIGIN
of type Origin
which denotes the point at the origin. This constant is used in the conversion between points and vectors. Subtracting it from a point p results in the locus vector of p.
In order to obtain the point corresponding to a vector v you simply have to add v to ORIGIN
. If you want to determine the point q in the middle between two points p1 and p2, you can write[1]
Note that these constructions do not involve any performance overhead for the conversion with the currently available representation classes.
CGAL中,严格区分了 point, vector 和 direction. 一个点是欧几里德空间Ed中的一个点,一个向量是向量空间Rd两个点p2, p1的差(difference),并指出从p1到p2的距离,另外一个向量如果不考虑其长度表示一个方向(direction)。它们是不同的数学概念。如:它们在仿射变换中的行为是不同的,且在仿射几何中两个点相加是无意义的。将它们放入不同的类中,我们不仅能得到较清晰的代码,也能够得到编译器的类型检查以避免模糊的表达。Hence, it pays twice to make this distinction.
CGAL定义了一个代表类型 Origin的
常量符号ORIGIN,这个符号指示初始的点。这个常量用于在点和向量之间进行转换。从一个点p中减去它将得到一个p的locus vector(轨迹向量????)。
3.2 Kernel Objects
Besides points (R::Point_d
), vectors (R::Vector_d
), and directions (R::Direction_d
), CGAL provides lines, rays, segments, hyperplanes, and spheres.
Lines (R::Line_d
) in CGAL are oriented. A ray (R::Ray_d
) is a semi-infinite interval on a line, and this line is oriented from the finite endpoint of this interval towards any other point in this interval. A segment (R::Segment_d
) is a bounded interval on a directed line, and the endpoints are ordered so that they induce the same direction as that of the line.
Hyperplanes are affine subspaces of dimension d−1 in Ed, passing through d points. Hyperplanes are oriented and partition space into a positive side and a negative side. In CGAL, there are no special classes for halfspaces. Halfspaces are supposed to be represented by oriented hyperplanes. All kernel objects are equality comparable via operator==
and operator!=
. For those oriented objects whose orientation can be reversed (segments, lines, hyperplanes, spheres) there is also a global function weak_equality()
that allows to test for point set equality disregarding the orientation.
除点(R::Point_d),向量(R::Vector_d)和方向 (R::Direction_d),CGAL提供了直线、射线、线段、超平面、和球体等。
CGAL中的直线(R::Line_d)是有方向性的。一个射线(R::Ray_d)是一个直线的半无限区间,它的方向是从射线的端点指向无限区间的任何一个点。线段(R::Segment_d)是一个有向直线上的有界区间( bounded interval),其端点顺序与直线的方向一致。
超平面是欧几里德空间Ed的d-1维仿射子空间,通过给定d个点可以确定 一个超平台,该超平面是有向的并将空间分为正半边和负半边。在CGAL中,没有半空间相对应的类。半空间由有向的超平面分别表示。所有的内核对象都可通过 == 和 != 进行相等比较。因为这些对象的方向可以反转(线段,直线,超平面和球体),所以提供了一个函数 weak_equality()
用于测试不考虑方向时的点集的相等关系。
3.3 Orientation and Relative Position
Geometric objects in CGAL have member functions that test the position of a point relative to the object. Full dimensional objects and their boundaries are represented by the same type, e.g. halfspaces and hyperplanes are not distinguished, neither are balls and spheres. Such objects split the ambient space into two full-dimensional parts, a bounded part and an unbounded part (e.g. spheres), or two unbounded parts (e.g. hyperplanes). By default these objects are oriented, i.e., one of the resulting parts is called the positive side, the other one is called the negative side. Both of these may be unbounded.
For these objects there is a function oriented_side()
that determines whether a test point is on the positive side, the negative side, or on the oriented boundary. These function returns a value of type Oriented_side
.
Those objects that split the space in a bounded and an unbounded part, have a member function bounded_side()
with return type Bounded_side
.
If an object is lower dimensional, e.g. a segment in d-dimensional space, there is only a test whether a point belongs to the object or not. This member function, which takes a point as an argument and returns a Boolean value, is called has_on()
CGAL几何对象提供了对于一个点相对于一个对象的相对位置进行判定的成员函数。全维对象和它们的边界由同样的类型表示,即半空间和超平面不作区分,即使是球体(ball and sphere)。这些对象将周围空间分割成两个全维的部分,一个有限部分和一个无限部分,缺省情况下,这些对象是有向的,一个是正向的,一个是负向的。两个部分都可能是无边界的。
对于这些物体,有一个函数oriented_side()
确定一个点是否在其正方向上、负方向上或在其边界上。这些函数返回一个Oriented_side类型的值。
对于将空间分为一个有限部分和一个无限部分的对象,有一个成员函数 bounded_side()
,它的返回类型是Bounded_side;
如果是一个低维度对象,即在d维空间内的线段,则只有一个测试函数来判定点是否在线段上。这个函数以点为参数,返回一个Boolean类型值,它的名字是has_on();
4 Predicates and Constructions
4.1 Predicates
Predicates are at the heart of a geometry kernel. They are basic units for the composition of geometric algorithms and encapsulate decisions. Hence their correctness is crucial for the control flow and hence for the correctness of an implementation of a geometric algorithm. CGAL uses the term predicate in a generalized sense. Not only components returning a Boolean value are called predicates but also components returning an enumeration type like a Comparison_result
or an Orientation
. We say components, because predicates are implemented both as functions and function objects (also called functors and provided by a kernel class).
CGAL provides predicates for the orientation of point sets (orientation
), for comparing points according to some given order, especially for comparing Cartesian coordinates (e.g. lexicographically_xy_smaller
), in-sphere tests, and predicates to compare distances.
判定(Predicates )是几何内核的核心部分。它们是几何算法和封装决定构成的基本单元。由此它们的正确性对于控制流和几何算法实现的正确性十分关键。CGAL基于归纳的思想使用Predicate术语。判定(Predicates )不仅包含返回Boolean值的成员组件(),也包括返回一个枚举值(如Comparison_result
)或一个方向(Orientation)的组件成员。我们说的组件成员,原因是判定的实现是函数或函数对象(由kernel类提供)。
CGAL提供了对一组点集的方向(orientation )的判定,提供了按照某个给定方向来比较点的判定,特别是提供了比较笛卡尔坐标(lexicographically_xy_smaller())、 in-sphere 的判定,以及比较距离的判定。
4.2 Constructions
Functions and function objects that generate objects that are neither of type bool
nor enum types are called constructions. Constructions involve computation of new numerical values and may be imprecise due to rounding errors unless a kernel with an exact number type is used.
Affine transformations (R::Aff_transformation_d
) allow to generate new object instances under arbitrary affine transformations. These transformations include translations, rotations (within planes) and scaling. Most of the geometric objects in a kernel have a member function transform(Aff_transformation_d t)
which applies the transformation to the object instance.
CGAL also provides a set of functions that detect or compute the intersection between objects and functions to calculate their squared distance. Moreover, some member functions of kernel objects are constructions.
So there are routines that compute the square of the Euclidean distance, but no routines that compute the distance itself. Why? First of all, the two values can be derived from each other quite easily (by taking the square root or taking the square). So, supplying only the one and not the other is only a minor inconvenience for the user. Second, often either value can be used. This is for example the case when (squared) distances are compared. Third, the library wants to stimulate the use of the squared distance instead of the distance. The squared distance can be computed in more cases and the computation is cheaper. We do this by not providing the perhaps more natural routine, The problem of a distance routine is that it needs the sqrt
operation. This has two drawbacks:
- The
sqrt
operation can be costly. Even if it is not very costly for a specific number type and platform, avoiding it is always cheaper. - There are number types on which no
sqrt
operation is defined, especially integer types and rationals.
生成非Boolean对象和非枚举对象的函数或函数对象称为构造(construction)。构造涉及到计算新的数字值和可能的舍入导致的不精确(除非使用一个精确的类型的核心)。
仿射变换(Affine transformations)((R::Aff_transformation_d
)允许任意仿射变换生成新的对象实例。这些变换包括位置变换、旋转(只在平面中)和缩放。内核的大部分几何对象有一个成员函数(transform(Aff_transformation t)),它对对象实例进行变换操作。
CGAL也提供一个函数集用来探测和计算2D kernel对象之间和很多3D kernel对象的交集,以及计算它们之间平方距离(squared distance)的函数。另外kernel对象的一些成员函数是构造(construction)。
所以,有一些函数来计算欧几里德距离的平方,但不存在计算距离本身的函数。原因是两者之间的转换是容易的。平方距离比较容易得到且开销不高。没有提供距离,则需要进行sqrt运算,这个运算有两个缺陷:
sqrt运算开销大,即使对于特定类型或特定平台它是较便宜的;
有些数类型上,sqrt操作已经定义,特别是整数类型和有理数类型中。
4.3 Intersections
Intersections on kernel objects currently cover only those objects that are part of flats (R::Segment_d
, R::Ray_d
, R::Line_d
, and R::Hyperplane_d
). For any pair of objects o1, o2 of these types the operation intersection(o1,o2)
returns a boost::optional< boost::variant< T... > >
where T...
is a list of all possible resulting geometric objects.
The exact result type of an intersection can be determined by using cpp11::result_of<Kernel::Intersect_d(Type1, Type2)>::type
where Type1
and Type2
are the types of the objects used in the intersection computation.
求内核对象交集的的运算目前只涉及到平面(objects that are part of flats)对象 (R::Segment_d
, R::Ray_d
, R::Line_d
, and R::Hyperplane_d
)。对于这些类的任何对象o1, o2,intersection(o1,o2)函数返回一个boost::optional< boost::variant< T... > >值,其中T...是所有可能结果几何体对象的列表。
4.3.1 Example
In the following example, the object type is used as a return value for the intersection computation, as there are possibly different return values.
4.4 Constructive Predicates
For testing where a point p lies with respect to a hyperplane defined by an array P of points p1, ... , pd, one may be tempted to construct the hyperplane R::Hyperplane_d(d,P,P+d)
and use the method oriented_side(p)
. This may pay off if many tests with respect to the plane are made. Nevertheless, unless the number type is exact, the constructed plane is only approximated, and round-off errors may lead oriented_side(p)
to return an orientation which is different from the orientation of p1, ... , pd, p.
In CGAL, we provide predicates in which such geometric decisions are made directly with a reference to the input points in P without an intermediary object like a plane. For the above test, the recommended way to get the result is to use orientation(P′,P′+d), where P′ is an array containing the points p1, ... , pd, p.
For exact number types like leda_real
, the situation is different. If several tests are to be made with the same plane, it pays off to construct the plane and to use oriented_side(p)
.
为了测试一个点p相对于由数组p( p1, ... , pd)确定的超平面的位置,我们可能会构造一个超平面 R::Hyperplane_d(d,P,P+d)
,并使用oriented_side(p)函数。该函数可能成功,如果一些关于该平面的测试被完成。但是,除非使用精确类型,否则创建的平面是不精确的,舍入误差可导致oriented_side(p)返回一个与真实的p1, ... , pd, p不同的方向。
在CGAL中,我们提供了一个判定,它直接使用输入点集做出几何决策,而不使用如平面等中间对象。对于上面的测试,我们推荐使用orientation(P′,P′+d),P′ 是一个包含了点集 p1, ... , pd, p的数组。
对于精确类型如leda_real,情况则不同。通过几个测试,它会成功地创建该平面并调用oriented_side(p)。
5 Design and Implementation History
This higher-dimensional kernel is the result of a long evolving development. A first version of the kernel was offered as a LEDA extension package ddgeo by Kurt Mehlhorn and Michael Seel. The original design was driven by the realization of a d-dimensional convex hull data type developed at the Max-Planck Institut für Informatik.
The code base was discussed and reviewed within the CGAL kernel group (of the low-dimensional kernel). This led to the identification of the concept interfaces and in parallel to adaptations according to the evolution of the low-dimensional kernel. The kernel was revised based on suggestions by Hervé Brönnimann, Michael Hoffmann, and Stefan Schirra.
Epick_d was added by Marc Glisse in 2014.
5.1 Acknowledgments
This work was supported by ESPRIT IV Long Term Research Projects No. 21957 (CGAL) and No. 28155 (GALIA).
The Epick_d kernel was partially supported by the IST Programme of the EU (FET Open) Project under Contract No IST-25582 – (CGL - Computational Geometric Learning).
- ^you might call
midpoint(p_1,p_2)
instead