clipper库使用的一些心得

clipper

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

1. 版本差异

之前工程里面使用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("\n");
	}
}

合并后的结果输出:

// [1th] : -10, -1 - 10, -1  10, 0  10, 0
// [2th] : -20, -1 - 20, -1  20, 0  20, 0

结果百思不得其解,结果怎么是一个线段了,莫名其妙???正确结果如下图,合并后是一个回字型。

不断地跟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为负。环绕数和填充规则的示例如下图:

为了表示一个带洞的多边形,例如上图中的回字型,需要内外两个路径表示,那么需要注意顶点的存储顺序吗? 这个问题的答案是,取决于多边形的填充规则。如果使用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("\n");
	}
}

不用care顶点顺序,效果图如下:

时间: 2024-10-29 21:24:42

clipper库使用的一些心得的相关文章

开发者必备的 12 个 JavaScript 库

现在 web 设计是最有趣的了,做好 web 设计不仅要熟练使用 Javascript,css 和 html 等,还要有自己的创意设计.为了方便大家发挥自己的创意,就产生了很多 JS 框架,Node.js 扩展等等.有了这些工具,开发者们就能专注于创意设计了,而不用为某个功能而花费太多精力.这里我们介绍的是 12 个开发者们必备的 JavaScript 库,都是一些很基础功能很强大的库.有了这些库,开发者们可以节省很多时间,大大提高开发的效率,所以大家赶紧收藏起来吧:) 1) Headroom.

如何建立适合产品的设计组件库?

组件库是设计系统里的一个重要分支,我们应该不会陌生.一个合适的组件库可以帮助设计师和开发者提高工作效率且让产品的可用性更高.风格更统一等等,那么今天我们就来聊聊应该如何建立适合的组件库. 发现问题 首先思考一个问题,什么样的情况下会需要建立组件库呢?建立组件库的目的是什么?带着这两个问题我们来看看下面的工作场景: 设计师:xxx把你之前的设计稿发我下,我要用里面的一个组件. 设计师:xxx为什么你的稿子里的颜色和我的不一致,到底应该以哪个为主??? 设计师:xxx为什么组件不统一??? 设计师:

pugixml的简单使用

一.简介 pugixml的官方主页为:http://pugixml.org/ pugixml是一个很棒的XML操作库, 它很轻量,只有三个文件(pugiconfig.hpp   pugixml.cpp  pugixml.hpp ) 支持Unicode 支持XPATH解析 速度快,仅比RapidXml慢一些 跨平台(windows/linux) 面向对象 Xml库解析性能比较表 (表格来自:http://rapidxml.sourceforge.net/manual.html) 二.配置 pugi

Python内置模块和第三方模块

1.Python内置模块和第三方模块 内置模块: Python中,安装好了Python后,本身就带有的库,就叫做Python的内置的库. 内置模块,也被称为Python的标准库. Python 2.x的在线库函数查询,可以去这里: The Python Standard Library 第三方库: 而非Python本身自带的库,就是所谓的第三方的库: 2.模块==库 模块,module,也常被叫做 库,Lib,Library. 3.常见的内置模块和第三方模块 Python中,一些常见的内置模块:

两本最近阅读的工具书的记录 关于Python和Linux命令行的 不喜勿喷 只是写给自己用

<Linux命令行完全技术宝典>读书心得 张栋作者 在学习Linux系统中,我们需要掌握各种管理的方法和技巧,而管理Linux系统最有效的方法就是命令行的控制.而我在图书馆中读到的<Linux命令行完全技术宝典>这本书就是对Linux中国所有所用到的常用命令的总结和讲解. 翻开这本书,我们会看到Linux的命令行在Linux管理中的重要作用.作者分别从命令行与GUI方式实例做比较,再去系统管理方面比较与GUI方式的优越性.让我在学习Linux命令行之前对命令行的重要性有一个大概的认

尝试造了个工具类库,名为 Diana

项目地址: diana 文档地址: http://muyunyun.cn/diana/ 造轮子的意义 为啥已经有如此多的前端工具类库还要自己造轮子呢?个人认为有以下几个观点吧: 定制性强,能根据自己的需求为主导延伸开发.万一一不小心还能帮到别人(比如 React 库): 纸上得来终觉浅,很多流行的库,只是照着它们的 API 进行使用,其实这些库里蕴含着大量的知识.技巧,最好的办法就是仿照它们来写些小 demo,从而体会这些库的精髓: 造轮子的过程中能让自己体会到与平常业务开发不一样的乐趣:比如和

mac下cocos2dx(带jsoncpp第三方库)编译为android项目心得

在mac下好不容写完了游戏,本以为轻轻松松就能编译为android项目,想不到弄了将近1整天才将问题解决 首先不带jsoncpp的编译方式请参考:http://www.bold-it.com/ios/cocos2d-x-box2d-iosandroid-hybrid-tutorial/ 带jsoncpp第三方库的参考了http://blog.csdn.net/sniffer12345/article/details/7336732,对作者表示感谢. 我使用的是正常的jsoncpp版本,在json

Tesseract 3.04 + VS2013 配置心得(包括静态库版本号和Release版本号)

研究Tesseract也有几个星期了 走了一些弯路 网上有非常多VS2010的配置心得 但没有VS2013的, 找到一篇之后, 又发现会有一些小问题, 这里记录下来, 也为新人提供一些帮助. Tesseract官网:https://code.google.com/p/tesseract-ocr/ 还是先说下Tesseract库的依赖架构: 有了这个库的依赖关系, 就能够说下自己配置VS2013版本号的目的了: a. 因为网上现有的都是现编译好的GIF, JPEG,PNG,TIFF,ZLIB库的D

视频动态库测试及心得

这几天一直在弄动态库测试,h给的写好的动态库--预处理模块的库.视频处理项目一部分,需要连接实际情况测试. 需求: 1.把实际相机连接到,并读取实时数据流,保存到双循环链表里面: 2.测试背景建模的功能效果: 3.预处理四个数据(图像)显示结果: 4.测试实时处理多摄像头效果显示. 前几天一直写摄像头数据接受模块,写的还算顺利,因为之前做过的项目自己写过DVR多通道的接受数据,而且还单独另写了读取网络摄像头的实时数据,所以一天搞定了,并显示原始图像正常. 由于h给了接口文档说明,直接调用了动态库