CGAL-多边形的表示-布尔运算-二维正则布尔集合操作

1.概述

在这里插入图片描述
该包包括在二维欧几里德空间中以弱x单调曲线为界的点集上实现布尔集合运算。特别是,它包含正则化布尔集合操作、交集谓词和点包容谓词的实现。上图显示了此类操作的简单示例。普通的布尔集合操作(区分多边形的内部和边界)在这个包中没有实现。第2章Nef多边形的布尔运算支持这些(线性)多边形的运算。在本章的其余部分,除非另有说明,我们将使用传统符号来表示正则化操作;例如,P∩Q表示P与Q的正则化交集。我们的包在两个点集P和Q上支持以下布尔集操作,每个点集都是一个或多个一般多边形的并集:
交集
计算交集R=P∩Q。
并集
计算并集R=P∪Q。
差集
计算差集R=P\Q。
对称差分
计算对称差分P=P⊕Q=(P\Q)∪(Q\P)。
补集
计算补集R= P ‾ \overline{\text{P}} P
交集谓词
检验两个集合P和Q是否重叠,区分三种可能的情形:(i)两个集合在它们的内部相交(即它们的正则相交不为空P∩Q≠∅);(ii)两个集合的边界相交,但其内部不相交;即它们有有限个数的共同点,甚至共享一条边界曲线(在这种情况下P∩Q=∅);(3)两个集合不相交。

2.术语和定义

在这里插入图片描述

多边形的例子。(a)简单多边形。(b)相对简单的多边形(c)既不简单也不相对简单的多边形。
多边形边缘和顶点交替的逆时针循环序列称为**多边形(外)边界**。

定义曲线内部不相交且顶点度数为2的多边形为简单多边形。这样的多边形具有良好定义的内部和外部,并且在拓扑上等同于盘形。请注意,在遍历上面(B)所示的相对简单的多边形的边缘时,没有曲线被交叉。

一个相对简单的多边形允许顶点的度数为>2,但其所有的边在其内部是不相交的。此外,它必须是一个可定向的多边形。也就是说,当它被插入到一个排列中并穿过它的外边界时,同一面与所有的半边相邻(在穿过过程中不会穿过任何曲线)。请注意,虽然多边形C与多边形B具有相同的曲线,但对曲线的穿越会导致穿过先前穿过的曲线,因此既不简单也不相对简单。

在我们的上下文中,多边形必须相对简单,其外部边界顶点必须围绕多边形的内部以逆时针方向排列。我们将多边形的概念推广到R2中具有多边形拓扑且其边界必须映射到x单调曲线的点集,并将其称为一般多边形。为了以后的简单,我们有时使用术语多边形来代替一般的多边形。

带孔的多边形表示一个点集,可以是有界的,也可以是无界的。在有界集合的情况下,它的外部边界被表示为一个相对简单(但不一定简单)的多边形,其顶点围绕集合的内部以逆时针方向排列。此外,该集合可能包含孔,其中每个孔表示为一个简单的多边形,其顶点围绕孔的内部按顺时针方向排列。请注意,一个没有孔的无界多边形横跨整个平面。孔的顶点可以与边界的顶点重合。

正则布尔集合运算op 可通过先取普通布尔集合运算(P op Q)的结果点集的内部,然后取闭包[1]得到。即P op Q=闭包(内部(P op Q))。正则布尔集合运算出现在构造实体几何(CSG)中,因为正则集在正则布尔集合运算下是封闭的,并且因为正则化消除了低维特征,即孤立的顶点和天线,从而简化并限制了对物理上有意义的实体的表示。我们的包提供了对多边形和一般多边形的正则化操作,其中一般多边形的边缘可以是一般的x单调曲线,而不是简单的线段。

2.1.有效多边形的条件

在我们的上下文中,多边形必须满足以下条件:

封闭边界-多边形的外部边界必须是一个连接的曲线序列,开始和结束于同一顶点。
简单性-多边形必须简单。
方向-多边形的外边界必须逆时针方向。

2.2.带孔多边形的有效条件

在我们的上下文中,带孔的多边形必须满足以下条件:

封闭边界-外边界和孔都必须是如上所定义的封闭多边形。
简单性-外部边界必须是一个相对简单的多边形(如上所定义)。每个孔必须是一个简单的多边形。
方向-带孔的多边形必须有一个逆时针方向的外边界。每个洞的外边界应该是顺时针方向
孔和外部边界必须是成对不相交的,除非在顶点上。
所有的孔都包含在外边界——孔必须包含在外边界内,并且必须与外边界不相交(顶点除外)。
两两不相交的孔(在内部)-多边形的孔必须是两两不相交的,除非在一个联合顶点/顶点上相交。

3.线性多边形的布尔集运算

CGAL的基本库包括 Polygon_2<Kernel,Container> 类模板,表示平面上的线性多边形。多边形由存储在Kernel::Point_2类型对象的容器中的顶点表示。多边形边缘是容器中相邻点之间的线段(Kernel::Segment_2对象)。默认情况下,容器是Kernel::Point_2对象的向量。
下面的函数演示了如何使用Polygon_2类的基本访问函数。它接受一个多边形P,并以可读的格式打印出来:

#include <iostream>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Boolean_set_operations_2.h>
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef Kernel::Point_2                                   Point_2;
typedef CGAL::Polygon_2<Kernel>                           Polygon_2;

template<class Kernel, class Container>
void print_polygon(const CGAL::Polygon_2<Kernel, Container>& P)
{
	typename CGAL::Polygon_2<Kernel, Container>::Vertex_const_iterator vit;
	std::cout << "[ " << P.size() << " vertices:";
	for (vit = P.vertices_begin(); vit != P.vertices_end(); ++vit)
		std::cout << " (" << *vit << ')';
	std::cout << " ]" << std::endl;
}

在本节中,我们使用术语polygon来表示Polygon_2实例,即具有线性边的多边形。一般多边形只在一般多边形的布尔集合运算部分讨论。
我们包的基本组件是自由(全局)函数complement(),它接受单个Polygon_2对象,intersection(), join(),difference(), symmetric_difference()和谓词do_intersect(),它们接受两个Polygon_2对象作为输入。我们将在下面几节中通过几个示例解释如何使用这些函数。

3.1.简单示例

在这里插入图片描述
测试两个多边形是否相交生成一个布尔值,并且不需要提供两个输入多边形之外的任何额外数据。下面列出的示例测试右侧描绘的两个三角形是否相交。和本章的其他示例程序一样,它使用了辅助头文件bso_rational_nt.h,它将Number_type类型定义为GMP的有理数类型(Gmpq),或者将其定义为数字类型Quotient<MP_Float>根据是否安装GMP库,在CGAL的支持库中包含。它还使用上面列出的print_polygon.h函数,该函数位于头文件print_utils.h中。

#include <iostream>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Boolean_set_operations_2.h>
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef Kernel::Point_2                                   Point_2;
typedef CGAL::Polygon_2<Kernel>                           Polygon_2;

template<class Kernel, class Container>
void print_polygon(const CGAL::Polygon_2<Kernel, Container>& P)
{
	typename CGAL::Polygon_2<Kernel, Container>::Vertex_const_iterator vit;
	std::cout << "[ " << P.size() << " vertices:";
	for (vit = P.vertices_begin(); vit != P.vertices_end(); ++vit)
		std::cout << " (" << *vit << ')';
	std::cout << " ]" << std::endl;
}

int main()
{
	Polygon_2 P;
	P.push_back(Point_2(-1, 1));
	P.push_back(Point_2(0, -1));
	P.push_back(Point_2(1, 1));
	std::cout << "P = "; print_polygon(P);
	Polygon_2 Q;
	Q.push_back(Point_2(-1, -1));
	Q.push_back(Point_2(1, -1));
	Q.push_back(Point_2(0, 1));
	std::cout << "Q = "; print_polygon(Q);
	if ((CGAL::do_intersect(P, Q)))
		std::cout << "The two polygons intersect in their interior." << std::endl;
	else
		std::cout << "The two polygons do not intersect." << std::endl;
	return 0;
}

3.2.带孔多边形

在这里插入图片描述

对简单多边形的操作。(a)两个多边形的结合,产生一个点集,其外部边界由一个简单多边形定义,并在其内部包含一个多边形孔。(b)两个多边形(浅阴影)相交(暗阴影),产生两个不相交的多边形。(c)一个简单多边形(浅阴影)的补(深阴影)。

在很多情况下,对两个没有孔的简单多边形进行二进制运算可能会得到一组内部有孔的多边形(见上图 (a)),或者一组不相交的多边形(见上图 (b));相似的集合可以由两个不相交的多边形的并(或对称差)产生。此外,简单多边形的补是包含一个孔的无界集合;见上图 ©。

正则集合在正则布尔集合操作下是封闭的。这些操作接受带孔的多边形作为输入,并可能产生输出。带孔的多边形表示一个点集,可以是有界的,也可以是无界的。在有界集合的情况下,它的外部边界被表示为一个相对简单(但不一定简单)的多边形,其顶点围绕集合的内部以逆时针方向排列。此外,该集合可能包含孔,其中每个孔表示为一个简单的多边形,其顶点围绕孔的内部按顺时针方向排列。请注意,一个没有孔的无界多边形横跨整个平面。孔的顶点可以与边界的顶点重合;请参见下面的示例。

用带孔多边形表示的点集被认为是闭合的。因此,孔的边界是集合的一部分(而不是孔的一部分)。通过布尔集合操作或一系列这样的操作所得到的带孔多边形的确切定义与正则布尔集合操作的定义密切相关,是相应普通操作内部的闭包,如下所述。
在这里插入图片描述
例如,考虑上图描述的正则集,它是三个小三角形适当转换的并集的结果。或者,可以通过取大三角形和小倒立三角形之间的差来得到相同的集合。一般来说,有很多方法可以到达一个特定的点集。然而,通过应用任意运算序列得到的带孔多边形集合是唯一的。右图所示的集合被表示为具有三角形外边界的单个多边形,其内部有一个三角形孔,而不是三个根本没有孔的三角形。一般来说,如果两个点集是连通的,那么它们属于同一个带孔的多边形。

类模板Polygon_with_holes_2<Kernel,Container>表示如上所述的带孔多边形,其中外边界和孔边界实现为Polygon_2<Kernel,Container>对象。给定一个Polygon_with_holes_2类的实例P,您可以使用谓词is_unbounded()来检查P是否是一个无界集合。如果它是有界的,您可以通过成员函数outer_boundary()获得代表其外部边界的逆时针方向多边形。您还可以通过holes_begin()和holes_end()遍历P的孔。这两个函数返回嵌套类型Polygon_with_holes_2::Hole_const_iterator的迭代器,它定义了P的孔的有效范围。这个迭代器的值类型是Polygon_2。

下面的函数演示了如何遍历带孔的多边形。它接受一个Polygon_with_holes_2对象,并使用辅助函数print_polygon()以可读格式打印其所有组件:

template<class Kernel, class Container>
void print_polygon_with_holes(const CGAL::Polygon_with_holes_2<Kernel, Container> & pwh)
{
	if (!pwh.is_unbounded()) {
		std::cout << "{ Outer boundary = ";
		print_polygon(pwh.outer_boundary());
	}
	else
		std::cout << "{ Unbounded polygon." << std::endl;
	typename CGAL::Polygon_with_holes_2<Kernel, Container>::Hole_const_iterator hit;
	unsigned int k = 1;
	std::cout << " " << pwh.number_of_holes() << " holes:" << std::endl;
	for (hit = pwh.holes_begin(); hit != pwh.holes_end(); ++hit, ++k) {
		std::cout << " Hole #" << k << " = ";
		print_polygon(*hit);
	}
	std::cout << " }" << std::endl;
}

因此,上述自由函数的简单版本接受两个Polygon_2对象P和Q作为它们的输入,而它们的输出是使用polygon with holes实例给出的:

简单多边形P的补总是具有一个多边形孔的无界集。函数complement§因此返回一个带孔的多边形对象,它表示P的补。
两个多边形P和Q的并集总是一个连通集,除非这两个输入多边形是完全不相交的。在后一种情况下,P∪Q由两个输入多边形组成。因此自由函数join(P, Q, R)返回一个布尔值,表示P∩Q≠∅。如果两个多边形不相交,它将带孔的多边形对象R(它通过引用接受)赋给正则并运算P∪Q的并集。
其他三个函数,即intersection(P, Q, oi), difference(P, Q, oi)和symmetric_difference(P, Q, oi),都有类似的接口。由于这些操作的结果可能由几个断开连接的组件组成,它们都接受一个输出迭代器oi,其值类型为Polygon_with_holes_2,并将输出的多边形添加到其相关的容器中。

3.3.简单多边形的连接和相交

下面的例子演示了自由函数join()和intersection()的用法,用于计算图3.2节开始图(b)中描述的两个简单多边形的并和交集。这个例子使用了上面列出的辅助函数print_polygon_with_holes(),它位于examples文件夹下的头文件print_utils.h中。

#include <iostream>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Boolean_set_operations_2.h>
#include <list>
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef Kernel::Point_2                                   Point_2;
typedef CGAL::Polygon_2<Kernel>                           Polygon_2;
typedef CGAL::Polygon_with_holes_2<Kernel>                Polygon_with_holes_2;
typedef std::list<Polygon_with_holes_2>                   Pwh_list_2;

template<class Kernel, class Container>
void print_polygon(const CGAL::Polygon_2<Kernel, Container>& P)
{
	typename CGAL::Polygon_2<Kernel, Container>::Vertex_const_iterator vit;
	std::cout << "[ " << P.size() << " vertices:";
	for (vit = P.vertices_begin(); vit != P.vertices_end(); ++vit)
		std::cout << " (" << *vit << ')';
	std::cout << " ]" << std::endl;
}

template<class Kernel, class Container>
void print_polygon_with_holes(const CGAL::Polygon_with_holes_2<Kernel, Container> & pwh)
{
	if (!pwh.is_unbounded()) {
		std::cout << "{ Outer boundary = ";
		print_polygon(pwh.outer_boundary());
	}
	else
		std::cout << "{ Unbounded polygon." << std::endl;
	typename CGAL::Polygon_with_holes_2<Kernel, Container>::Hole_const_iterator hit;
	unsigned int k = 1;
	std::cout << " " << pwh.number_of_holes() << " holes:" << std::endl;
	for (hit = pwh.holes_begin(); hit != pwh.holes_end(); ++hit, ++k) {
		std::cout << " Hole #" << k << " = ";
		print_polygon(*hit);
	}
	std::cout << " }" << std::endl;
}

int main()
{
	// Construct the two input polygons.
	Polygon_2 P;
	P.push_back(Point_2(0, 0));
	P.push_back(Point_2(5, 0));
	P.push_back(Point_2(3.5, 1.5));
	P.push_back(Point_2(2.5, 0.5));
	P.push_back(Point_2(1.5, 1.5));
	std::cout << "P = "; print_polygon(P);
	Polygon_2 Q;
	Q.push_back(Point_2(0, 2));
	Q.push_back(Point_2(1.5, 0.5));
	Q.push_back(Point_2(2.5, 1.5));
	Q.push_back(Point_2(3.5, 0.5));
	Q.push_back(Point_2(5, 2));
	std::cout << "Q = "; print_polygon(Q);
	// Compute the union of P and Q.
	Polygon_with_holes_2 unionR;//带洞多边形
	if (CGAL::join(P, Q, unionR)) {
		std::cout << "The union: ";
		print_polygon_with_holes(unionR);
	}
	else
		std::cout << "P and Q are disjoint and their union is trivial."
		<< std::endl;
	std::cout << std::endl;
	// Compute the intersection of P and Q. 交集计算
	Pwh_list_2                  intR;//带洞多边形列表
	Pwh_list_2::const_iterator  it;
	CGAL::intersection(P, Q, std::back_inserter(intR));
	std::cout << "The intersection:" << std::endl;
	for (it = intR.begin(); it != intR.end(); ++it) {
		std::cout << "--> ";
		print_polygon_with_holes(*it);
	}
	return 0;
}

3.4.带孔多边形的操作

我们已经证明了在简单多边形上执行布尔集合操作的自由函数可以输出带孔的多边形。除了这些函数之外,布尔集合操作包还提供了以下被覆盖的自由函数,它们接受General_polygon_with_holes_2对象作为它们的输入:complement()、intersection()、join()、difference()、symmetric_difference()和do_intersect()。大多数函数的原型与它们在简单多边形上操作的简单对应函数的原型相同。唯一的例外是complement(P, oi),它输出一系列带孔的多边形,这些多边形表示带孔P的多边形的补集。
在这里插入图片描述

#include <iostream>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Boolean_set_operations_2.h>
#include <list>

typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef Kernel::Point_2                                   Point_2;
typedef CGAL::Polygon_2<Kernel>                           Polygon_2;
typedef CGAL::Polygon_with_holes_2<Kernel>                Polygon_with_holes_2;
typedef std::list<Polygon_with_holes_2>                   Pwh_list_2;

template<class Kernel, class Container>
void print_polygon(const CGAL::Polygon_2<Kernel, Container>& P)
{
	typename CGAL::Polygon_2<Kernel, Container>::Vertex_const_iterator vit;
	std::cout << "[ " << P.size() << " vertices:";
	for (vit = P.vertices_begin(); vit != P.vertices_end(); ++vit)
		std::cout << " (" << *vit << ')';
	std::cout << " ]" << std::endl;
}

template<class Kernel, class Container>
void print_polygon_with_holes(const CGAL::Polygon_with_holes_2<Kernel, Container> & pwh)
{
	if (!pwh.is_unbounded()) {
		std::cout << "{ Outer boundary = ";
		print_polygon(pwh.outer_boundary());
	}
	else
		std::cout << "{ Unbounded polygon." << std::endl;
	typename CGAL::Polygon_with_holes_2<Kernel, Container>::Hole_const_iterator hit;
	unsigned int k = 1;
	std::cout << " " << pwh.number_of_holes() << " holes:" << std::endl;
	for (hit = pwh.holes_begin(); hit != pwh.holes_end(); ++hit, ++k) {
		std::cout << " Hole #" << k << " = ";
		print_polygon(*hit);
	}
	std::cout << " }" << std::endl;
}

int main()
{
	// Construct P - a bounded rectangle that contains a rectangular hole.
	Polygon_2 outP;
	Polygon_2 holesP[1];
	outP.push_back(Point_2(-3, -5));  outP.push_back(Point_2(3, -5));
	outP.push_back(Point_2(3, 5));    outP.push_back(Point_2(-3, 5));
	holesP[0].push_back(Point_2(-1, -3));
	holesP[0].push_back(Point_2(-1, 3));
	holesP[0].push_back(Point_2(1, 3));
	holesP[0].push_back(Point_2(1, -3));
	Polygon_with_holes_2  P(outP, holesP, holesP + 1);//带孔多边形的构造
	std::cout << "P = "; print_polygon_with_holes(P);
	// Construct Q - a bounded rectangle that contains a rectangular hole.
	Polygon_2 outQ;
	Polygon_2 holesQ[1];
	outQ.push_back(Point_2(-5, -3));  outQ.push_back(Point_2(5, -3));
	outQ.push_back(Point_2(5, 3));    outQ.push_back(Point_2(-5, 3));
	holesQ[0].push_back(Point_2(-3, -1));
	holesQ[0].push_back(Point_2(-3, 1));
	holesQ[0].push_back(Point_2(3, 1));
	holesQ[0].push_back(Point_2(3, -1));
	Polygon_with_holes_2  Q(outQ, holesQ, holesQ + 1);//带孔多边形的构造
	std::cout << "Q = "; print_polygon_with_holes(Q);
	// Compute the symmetric difference of P and Q.
	Pwh_list_2 symmR;
	Pwh_list_2::const_iterator it;
	CGAL::symmetric_difference(P, Q, std::back_inserter(symmR));
	std::cout << "The symmetric difference:" << std::endl;
	for (it = symmR.begin(); it != symmR.end(); ++it) {
		std::cout << "--> ";
		print_polygon_with_holes(*it);
	}
	return 0;
}

在某些情况下,将有孔的多边形的外边界与其内部的孔连接起来是很方便的。函数connect_holes()接受一个带有孔的多边形,并将每个孔的最顶层顶点与其正上方的多边形特征(外部边界的顶点或边缘,或另一个孔的顶点或边缘)连接起来。它产生一个输出序列的点,这些点与连接多边形中所有顶点的遍历相匹配(注意,还有额外的顶点,由垂直墙引起)。

#include <iostream>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Boolean_set_operations_2.h>. 
#include <list>

#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/connect_holes.h>
#include <list>
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef Kernel::Point_2                                   Point_2;
typedef CGAL::Polygon_2<Kernel>                           Polygon_2;
typedef CGAL::Polygon_with_holes_2<Kernel>                Polygon_with_holes_2;
int main(int argc, char* argv[])
{
	// Get the name of the input file from the command line, or use the default
	// pgn_holes.dat file if no command-line parameters are given.
	//more data files can be found under test data
	//boundary no other connections are made.
	const char* filename = (argc > 1) ? argv[1] : "pgn_holes.dat";
	std::ifstream input_file(filename);
	if (!input_file.is_open())
	{
		std::cerr << "Failed to open the " << filename << std::endl;
		return -1;
	}
	// Read a polygon with holes from a file.
	Polygon_2               outerP;
	unsigned int            num_holes;
	input_file >> outerP;
	input_file >> num_holes;
	std::vector<Polygon_2>  holes(num_holes);
	unsigned int            k;
	for (k = 0; k < num_holes; k++)
		input_file >> holes[k];
	Polygon_with_holes_2    P(outerP, holes.begin(), holes.end());
	// Connect the outer boundary of the polygon with its holes.
	std::list<Point_2>            pts;
	std::list<Point_2>::iterator  pit;
	connect_holes(P, std::back_inserter(pts));
	for (pit = pts.begin(); pit != pts.end(); ++pit)
		std::cout << '(' << *pit << ")  ";
	std::cout << std::endl;
	return (0);
}

3.5.多边形集操作

对两个多边形(或带孔的多边形)P和Q进行正则化操作的结果通常是几个不相连的带孔多边形的集合。用一个类来表示这样的集合是很自然的,这样就可以对计算得到的集合进行操作,例如P\Q。
布尔集合操作包中的一个核心组件是Polygon_set_2<Kernel, Container, Dcel>类模板。这个类的一个实例表示由几个不相连的带孔多边形的集合形成的点集。它使用Arrangement_2类将平面上的点集表示为平面排列;见第二章安排。实例化的cell类型用于表示底层的内部安排。它必须建模GeneralPolygonSetDcel概念,默认为Gps_default_dcel。您可以使用不同的DCEL类(通常是默认值的扩展)覆盖此默认值。只有当您打算获取底层内部安排并进一步处理它时,才需要重写默认值。
Polygon_set_2类的实例S通常表示应用于某些输入多边形的一系列操作的结果。S的表示是唯一的,不管为了得到它而应用的特定操作序列是什么。
此外,多边形集对象可以从单个多边形对象或从带孔多边形对象构造。一旦构造完成,就可以使用insert()方法将新的多边形(或带孔的多边形)插入到集合中,只要插入的多边形和集合中现有的多边形是不相交的。Polygon_set_2类还提供了访问函数,用于获取包含孔的多边形,以及一些查询。一个重要的查询是S.oriented_side(q),它确定查询点q是否包含在集合S的内部,是否位于集合的边界,或者两者都不包含。
类似于Polygon_set_2类模板,它适用于线性多边形(或带孔的多边形),General_polygon_set_2类适用于一般多边形(或带孔的一般多边形)。这些类模板中的每一个都将谓词do_intersect()和方法complement()、intersection()、join()、difference()和symmetric_difference()定义为成员函数。这些函数的操作数可以是简单多边形(Polygon_2或General_polygon_2对象),带孔多边形(Polygon_with_holes_2或General_polygon_with_holes_2对象),或多边形集(cpolygon_set_2或General_polygon_set_2对象)。前面提到的每个自由函数实际上都是通过一组重写成员函数实现的。
Polygon_set_2 (resp.General_polygon_set_2)的成员函数。执行布尔集合操作有两种风格:例如,S.join(P, Q)计算P和Q的并集并将结果赋值给S,而S.join§执行操作S⟵S∪P。同理,S.complement§将S设置为P的补,S.complement()将S赋值为其补。

3.6.集合操作序列

在章节Polygons with Holes中回顾的自由函数作为polygset类的包装器,提供这些函数只是为了方便。典型的这类函数构造一对General_polygon_set_2对象,调用适当的方法来应用所需的布尔运算,并将结果多边形集转换为所需的输出格式。因此,当在一个序列中执行多个操作时,直接使用General_polygon_set_2类型的成员函数要高效得多,因为从某些操作的内部表示中提取多边形,以及为后续操作重建内部表示可能会非常耗时。
在这里插入图片描述
下一个示例执行一个由三个布尔集合操作组成的序列。如上图所示,首先,计算两个简单多边形的并集,然后计算并集运算结果的补集。最后,它计算补运算的结果与矩形的交点,将最终结果限制在矩形的面积内。得到的集合S由两个部分组成:一个带孔的多边形和一个包含在这个孔内部的简单多边形。

#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Polygon_2.h>
#include <CGAL/Polygon_with_holes_2.h>
#include <CGAL/Polygon_set_2.h>
#include <list>
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef Kernel::Point_2                                   Point_2;
typedef CGAL::Polygon_2<Kernel>                           Polygon_2;
typedef CGAL::Polygon_with_holes_2<Kernel>                Polygon_with_holes_2;
typedef CGAL::Polygon_set_2<Kernel>                       Polygon_set_2;
template<class Kernel, class Container>
void print_polygon(const CGAL::Polygon_2<Kernel, Container>& P)
{
	typename CGAL::Polygon_2<Kernel, Container>::Vertex_const_iterator vit;
	std::cout << "[ " << P.size() << " vertices:";
	for (vit = P.vertices_begin(); vit != P.vertices_end(); ++vit)
		std::cout << " (" << *vit << ')';
	std::cout << " ]" << std::endl;
}

template<class Kernel, class Container>
void print_polygon_with_holes(const CGAL::Polygon_with_holes_2<Kernel, Container> & pwh)
{
	if (!pwh.is_unbounded()) {
		std::cout << "{ Outer boundary = ";
		print_polygon(pwh.outer_boundary());
	}
	else
		std::cout << "{ Unbounded polygon." << std::endl;
	typename CGAL::Polygon_with_holes_2<Kernel, Container>::Hole_const_iterator hit;
	unsigned int k = 1;
	std::cout << " " << pwh.number_of_holes() << " holes:" << std::endl;
	for (hit = pwh.holes_begin(); hit != pwh.holes_end(); ++hit, ++k) {
		std::cout << " Hole #" << k << " = ";
		print_polygon(*hit);
	}
	std::cout << " }" << std::endl;
}
int main()
{
	// Construct the two initial polygons and the clipping rectangle.
	Polygon_2 P;
	P.push_back(Point_2(0, 1));
	P.push_back(Point_2(2, 0));
	P.push_back(Point_2(1, 1));
	P.push_back(Point_2(2, 2));
	Polygon_2 Q;
	Q.push_back(Point_2(3, 1));
	Q.push_back(Point_2(1, 2));
	Q.push_back(Point_2(2, 1));
	Q.push_back(Point_2(1, 0));
	Polygon_2 rect;
	rect.push_back(Point_2(0, 0));
	rect.push_back(Point_2(3, 0));
	rect.push_back(Point_2(3, 2));
	rect.push_back(Point_2(0, 2));
	// Perform a sequence of operations.
	Polygon_set_2 S;
	S.insert(P);
	S.join(Q);                   // Compute the union of P and Q. P和Q的并集
	S.complement();               // Compute the complement. S的补集
	S.intersection(rect);        // Intersect with the clipping rectangle. S与rect交集
	// Print the result.
	std::list<Polygon_with_holes_2> res;
	std::list<Polygon_with_holes_2>::const_iterator it;
	std::cout << "The result contains " << S.number_of_polygons_with_holes()
		<< " components:" << std::endl;
	S.polygons_with_holes(std::back_inserter(res));
	for (it = res.begin(); it != res.end(); ++it) {
		std::cout << "--> ";
		print_polygon_with_holes(*it);
	}
	return 0;
}

3.7.插入不相交多边形

如果你想计算一个多边形P (P可能是一个简单多边形或带孔的多边形对象)与一个通用多边形集R的并集,并将结果存储在R中,你可以构造一个多边形集S§,并应用并集操作如下:

General_polygon_2 S (P);
R.join (S);

事实上,您可以直接应用联合操作:

R.join (P);

然而,如果你知道这个多边形不与R表示的任何一个多边形相交,你可以使用更有效的方法insert():

R.insert (P);

因为insert()假设P∩R=∅,所以它没有尝试计算P和R的边界之间的交集。与插入一个与R相交的不相交多边形相比,这一事实显著加快了插入过程。
insert()函数也是重载的,因此它也可以接受一系列多边形。当将一个多边形范围插入到一个多边形集S中时,该范围内的所有多边形和由S表示的多边形的内部必须是两两不相交的。

3.8.执行聚合操作

有几个选项来计算一组多边形P1,…Pm的并集。您可以按如下方式增量地执行此操作。每一步计算:
在这里插入图片描述
与Pk的并集,得到Sk。即,如果多边形集被给定为一对迭代器[begin, end],下面的循环计算它们在S中的并集。

InputIterator iter = begin;
Polygon_set_2 S (*iter);
while (++iter != end) {
  S.join (*iter);
  ++iter;
}

第二个选择是采用分而治之的方法。你将多边形的集合平分成两个集合。递归计算每个集合的并集,得到和中的部分结果,最后计算并集。然而,对于具有相对较少交集的稀疏多边形,使用第三种选项同时计算所有多边形的并集操作可以更有效地完成。这是通过构造所有输入多边形的平面排列,利用扫描线算法,然后从排列中提取结果来完成的。类似地,也可以聚合计算一组输入多边形的交集。
我们的包提供了免费的重载函数join()和intersection(),它们可以聚合计算一系列输入多边形的并集和交集。范围内的多边形没有限制;自然地,它们可能会相互交叉。包提供了重载的自由函数do_intersect(begin, end),用于确定两个输入迭代器[begin, end]定义的范围内的多边形是否相交。
General_polygon_set_2类还提供了等效的成员函数,这些成员函数可以对一系列输入多边形或带孔的多边形进行聚合操作。当这样的成员函数被调用时,由当前对象表示的一般多边形也被视为操作数。因此,你可以很容易地计算多边形范围的并集,如下所示:

Polygon_set_2 S;
S.join (begin, end);

3.9.Traits选择

所有应用布尔集合操作的自由函数模板都接受一个可选的traits参数;有关更多信息,请参见下一节。如果没有提供trait类,则选择一个可以处理包含输入多边形边界的曲线类型的默认类。您可以使用如下所述的模板参数以某种方式影响此选择。

处理(线性)多边形的自由函数模板集包括由模板参数UsePolylines参数化的重载版本。对于每个这样的函数模板,UsePolylines模板参数决定是否将每个输入(线性)多边形的边界视为单个(-单调)片段的循环序列或(-单调)折线的循环序列。默认情况下,UsePolylines被设置为CGAL::Tag_true,这意味着每个输入(线性)多边形的边界被视为(-单调)折线的循环序列。在大多数情况下,这种行为是优越的(即,更节省时间),因为作为平面扫描算法执行的一部分处理的事件数量减少了。在输入多边形的边界经常相交的情况下,将它们视为折线可能会降低效率。在这些情况下,用CGAL::Tag_false替换UsePolylines模板参数以恢复原始行为(其中每个输入线性多边形的边界被视为单个单调段的循环序列)。

布尔集合操作可以直接使用类模板General_polygon_set_2的实例应用于一般多边形(或带孔的一般多边形),或者通过使用一个自由函数,它在内部使用类模板General_polygon_set_2的实例。类似地,布尔集合操作可以直接应用于线性多边形(或带孔的线性多边形),使用类模板Polygon_set_2的实例或使用一个自由函数,该函数在内部默认使用类模板Polygon_set_2的实例。Polygon_set_2类模板的实例将多边形的每个边视为单个(单调)段,因为它反过来利用了Arr_segment_traits_2类模板;请参阅二维排列包中的线段和线性对象的特征类。
如果用CGAL::Tag_true替换UsePolylines模板参数,会出现以下情况:
不是使用Polygon_set_2类模板的实例,如上所述,使用General_polygon_set_2类模板的实例,它使用Arr_polyline_traits_2类模板;参见2D排列包中的Polyline和Polycurve特征类。
每个输入的线性多边形(分别为:带孔的线性多边形)转换为一般多边形(如:一般多边形(带孔),由单调折线包围。然后,生成的一般多边形(也以-单调折线为界)被转换回标准多边形。

4.一般多边形的布尔集合运算

在这里插入图片描述
在前面的章节中,只处理普通(线性)多边形。即以分段线性曲线为界的闭点集。布尔集合操作包允许对多边形边缘进行更一般的几何映射。该包提供的操作作用于一般曲线的-单调段(例如,二次曲线和多项式函数的段)为界的点集。例如,右图所示的点集是一个一般多边形,由两个单调的圆弧包围,它们分别对应于圆的下半部分和上半部分。
用拓扑学术语来说,一般多边形可以表示边界为严格简单曲线的任何简单连通点集。这样的多边形是GeneralPolygon_2概念的一个模型。这一概念的模型必须满足以下要求:

一般多边形是由一系列内不相交的偶对单调曲线构成的。这条曲线的目标点和范围内下一条曲线的源点(按循环顺序)必须重合,这样该点就定义了第个多边形顶点。
可以遍历构成一般多边形边缘的单调曲线。

在这里插入图片描述

定义GeneralPolygonWithHoles_2的方法与定义带孔的线性多边形的方法类似。这个概念的一个模型表示一个有界或无界的集合,它可以不是单连通的,并且必须提供以下操作:

表示外边界的一般多边形和表示孔的一系列一般多边形的构造。
访问表示外部边界的一般多边形(在有界集合的情况下)。
穿过这些洞。

在线性多边形的布尔集操作一节中,我们介绍了类Polygon_2和Polygon_with_holes_2,它们分别对GeneralPolygon_2和GeneralPolygonWithHoles_2的概念进行了建模。在本节中,我们将介绍这两个概念的其他模型。

中心类模板General_polygon_set_2<Traits,Dcel>用于表示由有限个带孔的不相交一般多边形组成的点集,并在这些点集上提供各种布尔集运算。它由一个trait类和一个DCEL类参数化。前者定义用于表示多边形顶点的点的类型和表示多边形边缘的单调曲线的类型。traits类还提供对这些类型的对象进行操作的基本几何操作。DCEL类用于表示底层的内部Arrangement_2数据结构。实例化的cell类型用于表示底层的内部安排。它必须建模GeneralPolygonSetDcel概念,默认为Gps_default_dcel。您可以使用不同的DCEL类(通常是默认值的扩展)覆盖此默认值。只有当您打算获取底层内部安排并进一步处理它时,才需要重写默认值。

一个实例化的General_polygon_set_2类定义了嵌套类型General_polygon_set_2<Traits,Dcel>::Polygon_2和General_polygon_set_2<Traits,Dcel>::Polygon_with_holes_2,它们分别对概念GeneralPolygon_2和GeneralPolygonWithHoles_2建模。

4.1.Traits类概念

用于实例化General_polygon_set_2类模板的traits类必须建模GeneralPolygonSetTraits_2的概念,并被裁剪以处理特定的曲线族。GeneralPolygonSetTraits_2概念细化了接下来指定的arrangementdirectionalx单调traits_2概念。

概念ArrangementDirectionalXMonotoneTraits_2重定义概念 ArrangementXMonotoneTraits_2 。因此,该概念的模型必须定义类型 X_monotone_curve_2,表示单调曲线,类型Point_2,表示平面点。这样的点可以是一条单调曲线的端点,也可以是两条曲线之间的交点。它还必须对基本概念列出的这些类型提供各种几何谓词和操作,例如确定一个点是位于单调曲线之上还是之下,计算两条曲线之间的交点等。注意,基本概念并不假设-单调曲线是定向的,单调曲线不需要有指定的源和目标,只需要确定给定曲线的左端点(按字典顺序较小)和右端点(按字典顺序较大)。

ArrangementDirectionalXMonotoneTraits_2概念将其单调曲线视为有向对象。因此,它需要对单调曲线进行两个额外的操作:

给定一条单调曲线,按字典顺序比较它的源点和目标点。
给定一条单调曲线,构造它的相反曲线(即交换它的源点和目标点)。

ar_segment_traits_2、Arr_non_caching_segment_traits_2、Arr_circle_segment_traits_2、Arr_conic_traits_2和Arr_rational_function_traits_2这些被捆绑在Arrangement_2包中并与CGAL一起发布的特性类,都是重定义概念ArrangementDirectionalXMonotoneTraits_2的模型。

就像使用 ArrangementXMonotoneTraits_2概念模型进行计算的情况一样,只有在使用精确算术时,操作才是健壮的。当使用不精确的算法时,(几乎)退化的配置可能导致程序异常终止甚至错误的结果。

4.2.圆弧多边形的操作

两个特性类与CGAL一起分布。第一个名为Gps_segment_traits_2,用于对普通多边形和带孔多边形执行布尔集合操作。实际上,Polygon_set_2这个类是General_polygon_set_2 <Gps_segment_traits_2>的专门化。这个类定义了它的多边形和带有孔的多边形类型,这样这个trait类的使用就被封装在polygon-set类中。

另一个预定义的traits类命名为Gps_circle_segment_traits_2<Kernel>并由几何CGAL核参数化。用这个trait类实例化General_polygon_set_2,得到了边界可以由线段和圆弧组成的多边形的表示。圆段特征类提供了非线性对象的谓词和结构,然而,它只使用有理数算术,因此非常高效。
在这里插入图片描述
下面的示例使用Gps_circle_segment_traits_2类来计算四个矩形和四个圆的并集。每个圆都表示为具有两个单调圆弧的一般多边形。合并是增量计算的,结果是一个有一个孔的多边形,如图所示。注意,由于这四个圆是不相交的,它们的联合是用插入方法计算的,而矩形的联合是用连接操作符计算的。

#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Gps_circle_segment_traits_2.h>
#include <CGAL/General_polygon_set_2.h>
#include <CGAL/Lazy_exact_nt.h>
#include <list>
#include <cassert>
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef Kernel::Point_2                                   Point_2;
typedef Kernel::Circle_2                                  Circle_2;
typedef CGAL::Gps_circle_segment_traits_2<Kernel>         Traits_2;
typedef CGAL::General_polygon_set_2<Traits_2>             Polygon_set_2;
typedef Traits_2::General_polygon_2                       Polygon_2;
typedef Traits_2::General_polygon_with_holes_2            Polygon_with_holes_2;
typedef Traits_2::Curve_2                                 Curve_2;
typedef Traits_2::X_monotone_curve_2                      X_monotone_curve_2;
// Construct a polygon from a circle.
Polygon_2 construct_polygon(const Circle_2& circle)
{
	// Subdivide the circle into two x-monotone arcs.
	Traits_2 traits;
	Curve_2 curve(circle);
	std::list<CGAL::Object>  objects;
	traits.make_x_monotone_2_object() (curve, std::back_inserter(objects));
	assert(objects.size() == 2);
	// Construct the polygon.
	Polygon_2 pgn;
	X_monotone_curve_2 arc;
	std::list<CGAL::Object>::iterator iter;
	for (iter = objects.begin(); iter != objects.end(); ++iter) {
		CGAL::assign(arc, *iter);
		pgn.push_back(arc);
	}
	return pgn;
}
// Construct a polygon from a rectangle.
Polygon_2 construct_polygon(const Point_2& p1, const Point_2& p2,
	const Point_2& p3, const Point_2& p4)
{
	Polygon_2 pgn;
	X_monotone_curve_2 s1(p1, p2);    pgn.push_back(s1);
	X_monotone_curve_2 s2(p2, p3);    pgn.push_back(s2);
	X_monotone_curve_2 s3(p3, p4);    pgn.push_back(s3);
	X_monotone_curve_2 s4(p4, p1);    pgn.push_back(s4);
	return pgn;
}
// The main program:
int main()
{
	// Insert four non-intersecting circles.
	Polygon_set_2 S;
	Polygon_2 circ1, circ2, circ3, circ4;
	circ1 = construct_polygon(Circle_2(Point_2(1, 1), 1));  S.insert(circ1);
	circ2 = construct_polygon(Circle_2(Point_2(5, 1), 1));  S.insert(circ2);
	circ3 = construct_polygon(Circle_2(Point_2(5, 5), 1));  S.insert(circ3);
	circ4 = construct_polygon(Circle_2(Point_2(1, 5), 1));  S.insert(circ4);
	// Compute the union with four rectangles incrementally.
	Polygon_2 rect1, rect2, rect3, rect4;
	rect1 = construct_polygon(Point_2(1, 0), Point_2(5, 0),
		Point_2(5, 2), Point_2(1, 2));
	S.join(rect1);
	rect2 = construct_polygon(Point_2(1, 4), Point_2(5, 4),
		Point_2(5, 6), Point_2(1, 6));
	S.join(rect2);
	rect3 = construct_polygon(Point_2(0, 1), Point_2(2, 1),
		Point_2(2, 5), Point_2(0, 5));
	S.join(rect3);
	rect4 = construct_polygon(Point_2(4, 1), Point_2(6, 1),
		Point_2(6, 5), Point_2(4, 5));
	S.join(rect4);
	// Print the output.
	std::list<Polygon_with_holes_2> res;
	S.polygons_with_holes(std::back_inserter(res));
	std::copy(res.begin(), res.end(),
		std::ostream_iterator<Polygon_with_holes_2>(std::cout, "\n"));
	std::cout << std::endl;
	return 0;
}

4.3.通用多边形集特性适配器-贝塞尔曲线

GeneralPolygon_2概念及其通用模型 General_polygon_2 单调特征;便于生成通用多边形集特征类。广义多边形(GeneralPolygon_2)概念的一个模型表示平面上以单调曲线为界的一个简单点集。与任何Traits类定义的普通Traits::Polygon_2类型相反,它必须定义类型X_monotone_curve_2,它表示点集边界的单调曲线。它必须从一系列这样的曲线中提供一个构造函数,以及一对方法,即curves_begin()和curves_end(),可用于迭代点集边界曲线。

类模板 General_polygon_2 单调特征;对GeneralPolygon_2的概念进行建模。它的唯一模板参数必须用概念ArrangementDirectionalXMonotoneTraits_2的模型实例化,它从中获得X_monotone_curve_2类型。它使用该模型对该类型的几何运算来维护类型为X_monotone_curve_2的有向曲线的容器,该有向曲线表示一般多边形的边界。

类模板 Gps_traits_2<ArrDirectionalXMonotoneTraits,GeneralPolygon> 实现 GeneralPolygonSetTraits_2模型的概念,并可以用来实例化类模板General_polygon_set_2。它充当几何特征类的适配器,该类为概念ArrangementDirectionalXMonotoneTraits_2建模。它可以用于在一般多边形上执行集合运算。适配器的实现相当简单,因为它是从继承其必要类型和方法的实例化模板参数ArrXMonotoneTraits_2派生而来的。它进一步利用了实例化参数GeneralPolygon提供的方法,该参数是GeneralPolygon_2概念的一个模型。默认情况下,GeneralPolygon参数定义为General_polygon_2<ArrangementDirectionalXMonotoneTraits_2>。

下面列出的代码摘录定义了一个通用多边形集合类型,可用于对由排列特征类Arr_traits_2定义的单调曲线类型所限定的点集执行布尔集合操作,Arr_traits_2是概念ArrangementDirectionalXMonotoneTraits_2的代表模型。

#include <CGAL/General_polygon_2.h>
#include <CGAL/Gps_traits_2.h>
typedef CGAL::General_polygon_2<Arr_traits_2> General_polygon_2;
typedef CGAL::Gps_traits_2<Arr_traits_2, General_polygon_2> Traits_2;
typedef CGAL::General_polygon_set_2<Traits_2> General_polygon_set_2;

在这里插入图片描述
采用贝塞尔曲线traits类Arr_Bezier_curve_traits_2将上面的排列-traits类Arr_traits_2实例化,结果得到一个通用多边形集合类型的定义,该类型可用于在由贝塞尔曲线限定的点集上执行布尔集合操作。
下一个示例分别计算两个一般多边形的交点,这些多边形由分别从两个输入文件读取的贝塞尔曲线限定。我们的示例使用的默认输入文件(char_g.dat和char_m.dat)分别定义了两个通用多边形,形状分别为字符g和m,使用Times New Roman字体。它们的交点由9个简单的多边形组成,如上图所示。
回想一下,每个贝塞尔曲线都是由一系列控制点定义的,这些控制点形成链(参见a节平面贝塞尔曲线的特征类)。每条曲线的最后一个控制点必须与其后继曲线的第一个控制点相同。示例中包含的read_Bezier_polygon()函数从输入文件中读取曲线,直到它们形成一个闭合链,该闭合链被认为是多边形的外部边界。如果有更多的曲线可用,它开始构建多边形,这些多边形对应于由外部边界包围的区域中的孔。注意,这个函数还负责按照Gps_traits_2适配器的要求,将输入贝塞尔曲线细分为单调子曲线。

#include <CGAL/config.h>
#ifndef CGAL_USE_CORE
#include <iostream>
int main()
{
	std::cout << "Sorry, this example needs CORE ..." << std::endl;
	return (0);
}
#else
#include <CGAL/Cartesian.h>
#include <CGAL/CORE_algebraic_number_traits.h>
#include <CGAL/Arr_Bezier_curve_traits_2.h>
#include <CGAL/Gps_traits_2.h>
#include <CGAL/Boolean_set_operations_2.h>
#include <CGAL/Timer.h>
#include <fstream>
typedef CGAL::CORE_algebraic_number_traits              Nt_traits;
typedef Nt_traits::Rational                             Rational;
typedef Nt_traits::Algebraic                            Algebraic;
typedef CGAL::Cartesian<Rational>                       Rat_kernel;
typedef CGAL::Cartesian<Algebraic>                      Alg_kernel;
typedef CGAL::Arr_Bezier_curve_traits_2<Rat_kernel, Alg_kernel, Nt_traits> Traits_2;
typedef Rat_kernel::Point_2                             Rat_point_2;
typedef Traits_2::Curve_2                               Bezier_curve_2;
typedef Traits_2::X_monotone_curve_2                    X_monotone_curve_2;
typedef CGAL::Gps_traits_2<Traits_2>                    Gps_traits_2;
typedef Gps_traits_2::General_polygon_2                 Polygon_2;
typedef Gps_traits_2::General_polygon_with_holes_2      Polygon_with_holes_2;
typedef std::list<Polygon_with_holes_2>                 Polygon_set;
bool read_Bezier_polygon(const char* filename, Polygon_with_holes_2& P)
{
	// Open the input file.
	std::ifstream   in_file(filename);
	if (!in_file.is_open())
		return false;
	// Read the number of curves.
	unsigned int      n_curves;
	unsigned int      k;
	in_file >> n_curves;
	// Read the curves one by one, and construct the general polygon these
	// curve form (the outer boundary and the holes inside it).
	Traits_2                    traits;
	Traits_2::Make_x_monotone_2 mk_x_monotone = traits.make_x_monotone_2_object();
	bool                        first = true;
	Rat_point_2                 p_0;
	std::list<X_monotone_curve_2>  xcvs;
	Rat_kernel                  ker;
	Rat_kernel::Equal_2         equal = ker.equal_2_object();
	std::list<Polygon_2>        pgns;
	for (k = 0; k < n_curves; k++) {
		// Read the current curve and subdivide it into x-monotone subcurves.
		Bezier_curve_2                           B;
		std::list<CGAL::Object>                  x_objs;
		std::list<CGAL::Object>::const_iterator  xoit;
		X_monotone_curve_2                       xcv;
		in_file >> B;
		mk_x_monotone(B, std::back_inserter(x_objs));
		for (xoit = x_objs.begin(); xoit != x_objs.end(); ++xoit) {
			if (CGAL::assign(xcv, *xoit))
				xcvs.push_back(xcv);
		}
		// Check if the current curve closes a polygon, namely whether it target
		// point (the last control point) equals the source of the first curve in
		// the current chain.
		if (!first) {
			if (equal(p_0, B.control_point(B.number_of_control_points() - 1))) {
				// Push a new polygon into the polygon list. Make sure that the polygon
				// is counterclockwise oriented if it represents the outer boundary
				// and clockwise oriented if it represents a hole.
				Polygon_2          pgn(xcvs.begin(), xcvs.end());
				CGAL::Orientation  orient = pgn.orientation();
				if ((pgns.empty() && (orient == CGAL::CLOCKWISE)) ||
					(!pgns.empty() && (orient == CGAL::COUNTERCLOCKWISE)))
					pgn.reverse_orientation();
				pgns.push_back(pgn);
				xcvs.clear();
				first = true;
			}
		}
		else {
			// This is the first curve in the chain - store its source point.
			p_0 = B.control_point(0);
			first = false;
		}
	}
	if (!xcvs.empty())
		return false;
	// Construct the polygon with holes.
	std::list<Polygon_2>::iterator     pit = pgns.begin();
	++pit;
	P = Polygon_with_holes_2(pgns.front(), pit, pgns.end());
	return true;
}
// The main program.
int main(int argc, char* argv[])
{
	// Get the name of the input files from the command line, or use the default
	// char_g.dat and char_m.dat files if no command-line parameters are given.
	const char* filename1 = (argc > 1) ? argv[1] : "char_g.dat";
	const char* filename2 = (argc > 2) ? argv[2] : "char_m.dat";
	// Read the general polygons from the input files.
	CGAL::Timer           timer;
	Polygon_with_holes_2  P1, P2;
	timer.start();
	if (!read_Bezier_polygon(filename1, P1)) {
		std::cerr << "Failed to read " << filename1 << " ..." << std::endl;
		return 1;
	}
	if (!read_Bezier_polygon(filename2, P2)) {
		std::cerr << "Failed to read " << filename2 << " ..." << std::endl;
		return 1;
	}
	timer.stop();
	std::cout << "Constructed the input polygons in " << timer.time()
		<< " seconds." << std::endl << std::endl;
	// Compute the intersection of the two polygons.
	Polygon_set                     R;
	Polygon_set::const_iterator     rit;
	timer.reset();
	timer.start();
	CGAL::intersection(P1, P2, std::back_inserter(R));
	timer.stop();
	std::cout << "The intersection polygons are of sizes: {";
	for (rit = R.begin(); rit != R.end(); ++rit)
		std::cout << ' ' << rit->outer_boundary().size();
	std::cout << " }" << std::endl;
	std::cout << "The intersection computation took "
		<< timer.time() << " seconds." << std::endl;
	return 0;
}
#endif

4.4.聚合操作

在执行聚合操作一节中,我们描述了如何将聚合并和交操作应用于普通多边形或带孔多边形的集合。当然,聚合操作也可以应用于一般多边形的集合。与普通多边形的情况一样,当输入多边形的交点数量与结果的复杂性具有相同的数量级时,建议使用聚合操作。如果不是这种情况,增量计算结果可能会更快。
在这里插入图片描述
下一个示例计算八个单位圆盘的并集,其中心距离原点有单位距离,如右侧所示。该示例还允许用户通过命令行提供不同数量的圆盘。

#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Gps_circle_segment_traits_2.h>
#include <CGAL/Boolean_set_operations_2.h>
#include <CGAL/Lazy_exact_nt.h>
#include <list>
#include <cstdlib>
#include <cmath>
#include <cassert>
typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel;
typedef Kernel::Point_2                                   Point_2;
typedef Kernel::Circle_2                                  Circle_2;
typedef CGAL::Gps_circle_segment_traits_2<Kernel>         Traits_2;
typedef Traits_2::Polygon_2                               Polygon_2;
typedef Traits_2::Polygon_with_holes_2                    Polygon_with_holes_2;
typedef Traits_2::Curve_2                                 Curve_2;
typedef Traits_2::X_monotone_curve_2                      X_monotone_curve_2;
// Construct a polygon from a circle.
Polygon_2 construct_polygon(const Circle_2& circle)
{
	// Subdivide the circle into two x-monotone arcs.
	Traits_2 traits;
	Curve_2 curve(circle);
	std::list<CGAL::Object> objects;
	traits.make_x_monotone_2_object() (curve, std::back_inserter(objects));
	assert(objects.size() == 2);
	// Construct the polygon.
	Polygon_2 pgn;
	X_monotone_curve_2 arc;
	std::list<CGAL::Object>::iterator iter;
	for (iter = objects.begin(); iter != objects.end(); ++iter) {
		CGAL::assign(arc, *iter);
		pgn.push_back(arc);
	}
	return pgn;
}
// The main program:
int main(int argc, char* argv[])
{
	// Read the number of circles from the command line.
	unsigned int n_circles = 8;
	if (argc > 1) n_circles = std::atoi(argv[1]);
	// Create the circles, equally spaced of the circle x^2 + y^2 = 1.
	const double pi = std::atan(1.0) * 4;
	const double n_circles_reciep = 1.0 / n_circles;
	const double radius = 1;
	const double frac = 2 * pi * n_circles_reciep;
	std::list<Polygon_2> circles;
	unsigned int k;
	for (k = 0; k < n_circles; k++) {
		const double angle = frac * k;
		const double x = radius * std::sin(angle);
		const double y = radius * std::cos(angle);
		Point_2 center = Point_2(x, y);
		Circle_2 circle(center, radius);
		circles.push_back(construct_polygon(circle));
	}
	// Compute the union aggregately.
	std::list<Polygon_with_holes_2> res;
	CGAL::join(circles.begin(), circles.end(), std::back_inserter(res));
	// Print the result.
	std::copy(res.begin(), res.end(),
		std::ostream_iterator<Polygon_with_holes_2>(std::cout, "\n"));
	std::cout << std::endl;
	return 0;
}

5.总结

本文介绍CGAL中二维布尔多边形运算,惊奇的发现不但支持普通曲线多边形,还支持圆,贝塞尔等特殊多边形。

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
CGAL是一个计算几何的C++库,其提供了许多用于算法设计和实现的数据结构和函数。其中就包括了二维平面多边型的布尔运算操作,包括求交集、并集、差集等。这些运算在计算机图形学、地理信息系统、自动化设计等领域都有广泛的应用。 在测试CGAL二维平面多边形布尔运算性能时,需要考虑多个因素,如多边形的大小、数量、形状以及计算机的硬件配置等。我将分别从这些方面来介绍CGAL二维平面多边形布尔运算性能测试的结果。 1. 多边形的大小和数量 测试表明,CGAL能够处理非常大的多边形,例如含有上万个点的多边形。然而,多边形的大小和数量还是会对运算时间产生影响。当多边形数目增加时,运算时间也相应增加。实验表明,当多边形数量为几百到几千时,CGAL处理多边形布尔运算的时间较长,需要几秒钟到几分钟不等。当多边形数量超过一万时,运算时间将变得非常长,可能需要花费数十分钟的时间。 2. 多边形的形状 当多边形的形状很简单时,例如正多边形和矩形,CGAL的运算时间非常短,可以在毫秒或几秒钟内完成。但如果多边形有大量的凹角和复杂的几何形状,CGAL的运算时间将变长。实验表明,当多边形具有纽结、孔洞等复杂形状时,CGAL的运算时间可能会增加几倍或更多。 3. 计算机硬件配置 CGAL二维平面多边形布尔运算的性能还与计算机硬件配置有关。例如,处理大型多边形时,需要更快的处理器和更大的内存。此外,CGAL还可以利用多个处理器来并行处理大型多边形,这将极大地提高计算速度。 综上所述,CGAL二维平面多边形布尔运算的性能测试结果取决于多方面因素。在对运算性能进行测试时,需要综合考虑多边形的大小、数量、形状以及计算机的硬件配置等因素。当然,在实际应用中,还需要根据具体情况选择最适合自己的算法和数据结构。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值