clipper库使用的一些心得

摘要:
使用的多边形将替换为路径。当使用Clipper::AddPath时,需要确定是否关闭它。不断地与裁剪器提供的演示程序进行比较,最终发现问题:问题在于裁剪器内部的IntPoint,假设宏的使用未定义_ int32上面代码中的printf为%d。请参阅OpenGL红皮书中关于多边形填充规则的说明:http://glprogramming.com/red/chapter11.html多边形填充规则的使用引入了包围数和包围规则的概念。如果使用EvenOdd规则,则不考虑顶点的存储顺序。

clipper 

sourceforge官网:http://sourceforge.net/projects/polyclipping/


1. 版本号差异

之前project里面使用4.8.6,近期升级到最新版本号6.2.1。接口层面有点区别:

老版本号使用Polygon概念,最新版本号用Path取代了Polygon。对用的Polygons用Paths取代,Clipper::AddPath的时候还须要制定是否封闭


2. 注意数据类型

一个測试,回字上半部分和下半部分,两半部分进行合并,可是输出结果总是不正确:

void transform_array_to_path(int* arr, int size, ClipperLib::Path& path, int scale = 1)
{
	for (int i = 0; i < size; i += 2)
	{
		path.push_back(ClipperLib::IntPoint(arr[i] * scale, arr[i + 1] * scale));
	}
}

void ClipperTest::merge_case()
{
	using namespace ClipperLib;

	Clipper union_worker;
	Paths solution;

	Path positive_path;
	{
		int points[] = { 1, 1, 1, 0, 2, 0, 2, 2, -2, 2, -2, 0, -1, 0, -1, 1 };
		transform_array_to_path(points, sizeof(points) / sizeof(points[0]), positive_path, 10);
	}

	union_worker.AddPath(positive_path, ClipperLib::ptSubject, true);

	Path negative_path;
	{
		int points[] = { 1, -1, 1, 0, 2, 0, 2, -2, -2, -2, -2, 0, -1, 0, -1, -1 };
		transform_array_to_path(points, sizeof(points) / sizeof(points[0]), negative_path, 10);
	}

	union_worker.AddPath(negative_path, ClipperLib::ptClip, true);

	union_worker.Execute(ClipperLib::ctUnion, solution, pftEvenOdd, pftEvenOdd);

	for (int k = 0; k < solution.size(); k++)
	{
		Path& path = solution[k];
		
		printf("[ %dth ] : ", k + 1);

		for (int t = 0; t < path.size(); t++)
		{
			printf("%d,%d  ", path[t].X, path[t].Y);
		} 
		printf("
");		
	}
}
合并后的结果输出:

// [1th] : -10, -1 - 10, -1  10, 0  10, 0
// [2th] : -20, -1 - 20, -1  20, 0  20, 0
结果百思不得其解,结果怎么是一个线段了。莫名其妙???正确结果例如以下图。合并后是一个回字型。

clipper库使用的一些心得第1张

不断地跟clipper自带的demo程序比对,最终发现了问题所在:问题出在Clipper内部的IntPoint,假设未定义宏use_int32。採用的是long long存储顶点XY值。而上面code中printf是%d。使用%lld或者cout 就没问题了。坑啊。。。


2. 带洞多边形和多边形填充规则

clipper中定义了,EvenOdd,NonZero。Positive。Negative四中填充规则。相应參考OpenGL红皮书上关于多边形填充规则的说明:http://glprogramming.com/red/chapter11.html

多边形填充规则的使用引入了一个围绕数(Winding Numbers)和围绕规则(Winding Rules)的概念。围绕规则一般CCW为正。CW为负。

围绕数和填充规则的示比例如以下图:

clipper库使用的一些心得第2张

为了表示一个带洞的多边形,比如上图中的回字型。须要内外两个路径表示,那么须要注意顶点的存储顺序吗? 这个问题的答案是,取决于多边形的填充规则。假设使用EvenOdd规则,则不用关心顶点的存储顺序。由于:第一圈为+1/-1,一定是奇数,然后加1或者减1,结果都是偶数。然后再加1或减1结果一定是奇数

有了这个认识。我们写个測试样例,一个回字。跟一个四边形即可融合,Subject是一个Paths。包括两个Path表示。内外圈顺序无关;Clip是一个Path。进行合并的结果包括两个Path。
void ClipperTest::polygon_with_hole_merge_test()
{
	using namespace ClipperLib;	
	
	Path path1_outer;
	Path path1_inner;
	{
		int outer[] = { -2, -2, 2, -2, 2, 2, -2, 2 };
		int inner[] = { -1, -1, 1, -1, 1, 1, -1, 1 };		
		transform_array_to_path(outer, sizeof(outer)/sizeof(outer[0]), path1_outer);
		transform_array_to_path(inner, sizeof(inner)/sizeof(inner[0]), path1_inner);
	}

	Path path2;
	{
		int outer[] = { 2, 2, 3, 2, 3, -2, 2, -2 };
		transform_array_to_path(outer, sizeof(outer) / sizeof(outer[0]), path2);
	}

	Paths sub_poly;
	sub_poly.push_back(path1_outer);
	sub_poly.push_back(path1_inner);

	Clipper union_worker;
	union_worker.AddPaths(sub_poly, ptSubject, true);
	union_worker.AddPath(path2, ptClip, true);

	Paths solution;
	union_worker.Execute(ClipperLib::ctUnion, solution, pftEvenOdd, pftEvenOdd);

	for (int k = 0; k < solution.size(); k++)
	{
		Path& path = solution[k];

		printf("[ %dth ] : ", k + 1);

		for (int t = 0; t < path.size(); t++)
		{
			// printf("%d,%d  ", path[t].X, path[t].Y);
			cout << path[t].X << "," << path[t].Y << " ";
		} printf("
");
	}
}
不用care顶点顺序,效果图例如以下:
clipper库使用的一些心得第3张


免责声明:文章转载自《clipper库使用的一些心得》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇如何在sql server的group by语句中聚合字符串字段ES5和ES6对象导出和导入(转载,待整理)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

同时安装PyQt4和PyQt5之sip版本区分

如果同时安装了PyQt4和PyQt5(自己编译或者安装预编译版本),在运行某个PyQt4或者PyQt5的Python脚本时,很有可能出现以下的错误提示: RuntimeError: the sip module implements API vX.X but the PyQt5.QtCore module requires API vY.Y 本文帮助大家解...

Elasticsearch增删改查 之 —— Update更新

更新操作,一般用这个的,应该不会很多吧!ES本身还是一个倾向于查询检索的框架,对于这种更新的操作,太过频繁总归是不好的。不过阅读本篇后,你可以使用Script对所有的文档执行更新操作,也可以使用doc对部分文档执行更新,也可以使用upsert对不存在的文档执行添加操作。 更新 Update更新操作允许ES获得某个指定的文档,可以通过脚本等操作对该文档进行更...

关于c# dll版本号的修改方法

方法都是从网上找到的,自己把使用过的记录下,以备回忆。 程序集版本有4部分组成, // 主版本 // 次版本 // 内部版本号// 修订号 方法1:打开 类库对应的 AssemblyInfo.cs 文件            1.注释[assembly: AssemblyFileVersion("1.0.0.0")]            2.[asse...

配置管理工具比較

配置管理工具比較怎样选择配置管理工具  每一个软件项目,不管是project类项目,还是产品类项目,都必须经历需求分析、系统设计、编码实现、集成測试、部署、交付、维护和支持的过程。在这个过程中,将生成各种各样不同的工件,包含文档、源程序、可执行代码、支持库。更可怕的是,频繁出现的变更是不可避免的,因此面向如此庞大且不断变动的信息集,怎样使其有序、高效地存...

appium---命令行启动appium

  在客户端的appium长时间运行的时候,出产生一些数据、日志有可能会对appium的内存有所增长,严重的会使appium产生崩溃,这个时候就推荐使用通过cmd进行运行appium, 安装前提需要安装NodeJS,npm NodeJS安装 下载官网地址:点击链接;   2.傻瓜式安装(一直点击下一步) Appium安装 通过npm进行安装 注意:因为是...

ssh应用层协议原理 jsch源码探究 authfail

公司的项目需要用java实现远程登录主机,需要用的jsch jar包,但是在调用此jar包的接口的时候,总是会不经意间出现一些错误: auth faile ; 但传递的用户名和密码是正确的,这可如何是好,由于这个远程方问接口是由我维护的,所以现在这个侦查原因的重任就落在的我的身上。起初通过debug 跟踪代码发现是在创建session时抛出的异常,可是这个...