clipper庫使用的一些心得


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("\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頂點順序,效果圖例如以下:




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM