给定一组平面曲线C,arrangement 将平面细分成零维,一维,二维单元,称为顶点,边和面, Arrangements 在计算几何中无处不在并有广泛的应用。
C中的曲线可以彼此相交(一条曲线也可以是自相交的,也可以是由几个不相连的分支组成的),而且不一定是x单调的*1。我们用如下两步构造一个C”集合,它是由内部成对不相交的x-单调子曲线组成的。首先,我们将C中的每条曲线分解为极大的x-单调子曲线(以及可能孤立的点),得到集合C ',注意x-单调曲线不能是自交的。然后,我们将C '中的每条曲线分解为C '中不与任何其他曲线(或点)相交的最大连通子曲线。如果C的曲线包含孤立点,。集合C”也可能包含孤立的点。集合C”产生的arrangement 可以方便地嵌入成一个平面图(planar graph),其顶点与曲线端点或孤立点相关联,其边与子曲线相关联。显而易见:。这个图(graph)可以用双连接边表数据结构(DCEL)来表示。
DCEL数据结的主要思想是使用一对有向半边来表示每条边,由于每个半边是有向的,我们说它有一个源顶点(source)和一个目标顶点(target)。半边缘用于分离面,连接顶点(孤立的顶点除外,它是不连接的)。
如果顶点v是半边e的目标(target),我们说v和e是相互关联的。关联到顶点v的半边形成一个围绕这个顶点顺时针方向的循环链表。
每个半边e存储一个指向其关联面(ncident face)(位于其左侧的面)的指针。此外,每一个半边之后都有另一个共享同一关联面的半边,使得该半边的目标顶点与下一个半边的源顶点相同。因此,半边在循环链表链接, 并形成链,使链的所有边都关联同一面并沿其边界缠绕。我们称这样的链为边界的连接组件(简称CCB)。
沿面的边界以逆时针方向环绕的半边称为面的外CCB。我们暂时只考虑有界曲线的排列(arrangement ),以便在每一排列中都恰好存在一个无界面。无边界面没有外部边界。面的边界上的任何其他连接部件称为孔(或inner CCB),并且可以表示为沿顺时针方向绕着它的半边环绕。注意,一个孔不一定对应于一个单一的面,因为它可能没有面积,或者它可能由几个连通面的组成。每一个面的内部都可能有几个洞(或者根本没有洞)。此外,每个面在其内部可能包含孤立的顶点。
图1. 一种内部不相交线段的排列,用一些表示线段的DCEL记录来表示。无界面f0有一个单独的连接组件,在它里面形成一个孔,这个孔由几个面组成。半边e从源顶点v1指向目标顶点v2,这条边,连同它的孪生边e '对应于一条线段,连接与v1和v2相关的点,并将面f1和f2分离。e的前驱和后继是构成面f2外边界的链的一部分。面f1有一个更复杂的结构,因为它包含两个洞在它的内部:一个孔由两个相邻的面f3和f4组成,另一个孔由两条边组成。f1的内部也包含两个孤立的顶点u1和u2。
arrangement 的x-单调曲线嵌入一个称为参数空间的矩形二维区域。参数空间定义为X×Y,其中X和Y是端点在紧实直线上的开、半开或闭区间。令,,,和定义为X和Y的端点,我们通常将这些值称为参数空间边界的左、右、下和上边界。例如,如果参数空间是整个紧致平面,这是包当前支持的唯一选项,
本章的其余部分组织如下:在 The Main Arrangement Class 一节中,我们详细回顾了 Arrangement_2
类模板的接口,它是Arrangement包中的中心组件。在“Issuing Queries on an Arrangement ”一节中,我们展示了如何发出Arrangement上的查询。在Arrangement包中的Free Functions in the Arrangement Package 一节中,我们回顾了一些重要的对Arrangement进行操作的自由(全局)函数,其中最重要的是自由插入函数。节 Traits Classes 包含了Arrangement包中包含的各种几何特征类的详细描述。利用这些特征类,可以构造不同曲线族的排列。在 The Notification Mechanism一节中,我们将回顾允许外部类跟踪Arrangement实例所经历的更改的通知机制。Extending the DCEL一节解释了如何扩展DCEL记录,如何用它们存储额外的数据,以及如何有效地更新这些数据。在 Overlaying Arrangements一节中,我们介绍叠加两种Arrangement的基本操作。节 Storing the Curve History 描述了Arrangement_with_history_2类模板,它通过在其曲线中存储额外的历史记录来扩展排列。最后,在“ Input/Output Streams”一节中,我们将回顾输入/输出函数的安排。
类 Arrangement_2
是Arrangement包中的主类。它用于表示平面Arrangement,并提供了构建它们、遍历它们和维护它们所需的接口。Arrangement由几何特征类和DCEL类定义,几何特征类决定形成Arrangement的平面曲线族,DCEL类表示平面细分的拓扑结构。它提供了最少的几何操作集(谓词和构造),用于构造和维护Arrangement并对其进行操作。
arrangement包的设计是根据需要将arrangement的表示和对其进行操作的各种几何算法分离开来的,以及将平面细分的拓扑和几何方面分离开来的。这种分离由 Arrangement_2
模板的两个模板参数表现出来:
ArrangementBasicTraits_2
概念的模型和其他可选的几何特征概念来实例化。ArrangementBasicTraits_2概念的模型定义了x-单调曲线和二维点的类型,分别是X_monotone_curve_2
和 Point_2
,并支持它们的基本几何谓词。在本章的第一节中,我们总是使用Arr_segment_traits_2
作为我们的traits类,来构造线段的Arrangement。然而,Arrangement包包含其他几个特征类,可以处理其他类型的曲线,如折线(连续的分段线性曲线)、二次曲线和有理函数的曲线。我们在Section Traits Classes.中举例说明了这些trait类的用法。下面列出的简单程序构造了一个由三条线段组成的三角形的平面map。arrangement 的构造是用Arr_segment_traits_2 traits类实例化的,只处理段(segment)。所得到的arrangement 包括两个面,一个有界三角形面和一个无界三角形面。
#include
#include
#include
#include
#include
typedef CGAL::Quotient Number_type;
typedef CGAL::Cartesian Kernel;
typedef CGAL::Arr_segment_traits_2 Traits_2;
typedef Traits_2::Point_2 Point_2;
typedef Traits_2::X_monotone_curve_2 Segment_2;
typedef CGAL::Arrangement_2 Arrangement_2;
int main()
{
Arrangement_2 arr;
Segment_2 cv[3];
Point_2 p1 (0, 0), p2 (0, 4), p3 (4, 0);
cv[0] = Segment_2 (p1, p2);
cv[1] = Segment_2 (p2, p3);
cv[2] = Segment_2 (p3, p1);
CGAL::insert (arr, &cv[0], &cv[3]);
return (0);
}
最简单和最基本的arrangement 操作是各种遍历方法,它允许用户系统地查看手边的arrangement 的相关特性。如上所述,这种arrangement 被表示为一个DCEL,它存储了三个容器:顶点、半边和面。因此,Arrangement_2类为这些容器提供了迭代器。例如,vertices_begin()和vertices_end()方法返回的对象是arrange顶点有效范围内的Arrangement_2::Vertex_iterator对象,该迭代器的取值类型是 Arrangement_2::Vertex,
此外,顶点迭代器类型等效于Arrangement_2::Vertex_handle,它用作指向顶点的指针。所有与arrangement 特性相关的函数都接受句柄类型作为输入参数,并返回句柄类型作为输出。
除了用于arrangement 顶点、半边和面的迭代器外,arrangement类还提供了edges_begin()和edges_end(),它们返回用于遍历arrangement 边的Arrangement_2::Edge_iterator对象。注意,该迭代器的值类型为arrangement::Halfedge,表示表示边的两个半边中的一个。
一个顶点总是与一个几何实体相关联,即与一个Point_2对象相关联,这个对象可以通过嵌套在Arrangement_2中的vertex类的point()方法获得。方法is_isolated()的作用是:确定一个顶点是否是孤立点。incident_halfedges()方法返回一个类型为Arrangement_2:: Halfedge_around_vertex_circulator 的循环器,它允许沿顺时针方向遍历这个循环列表。这个循环器的值类型是halffedge。下面的函数打印给定arrangement 顶点的所有邻居(假设可以使用<<操作符将Point_2类型插入到标准输出中)。arrangement 类型与上面的简单示例相同。
void print_neighboring_vertices (Arrangement_2::Vertex_const_handle v)
{
if (v->is_isolated()) {
std::cout << "The vertex (" << v->point() << ") is isolated" << std::endl;
return;
}
Arrangement_2::Halfedge_around_vertex_const_circulator first, curr;
first = curr = v->incident_halfedges();
std::cout << "The neighbors of the vertex (" << v->point() << ") are:";
do {
// Note that the current halfedge is directed from u to v:
Arrangement_2::Vertex_const_handle u = curr->source();
std::cout << " (" << u->point() << ")";
} while (++curr != first);
std::cout << std::endl;
}
注释:*1 如果每一条垂直线与平面曲线C最多相交一次,那么连续的平面曲线C是x单调的。