凸包生成算法

凸包生成算法(Andrew算法)

一、前言

在碰撞检测算法中,最为重要的理论基础都是凸包,在游戏领域也经常用到碰撞的思想,在三维几何算法中也经常用到,凸包可以视为最小最紧凑的包围体,很多碰撞的检测算法中,如a物体与b物体是否发生碰撞,以便做出不同的功能,如子弹是否打中目标等。多种凸包求解算法中,比较经典的两种算法莫过于:Andrew算法和Quickhull算法了。本人在三维几何模型算法处理过程中就需要手写凸包算法,顺便记录下来,本文实现的是Andrew算法。

二、算法原理

算法过程如图所示:

  1. 先生成随机点
  2. 将随机点根据x轴排序
  3. 第一次遍历将上半部分凸包找到
  4. 第二次遍历将下半部分凸包找到
  5. 按照瞬时间或者逆时针的方式排列好
  • 以上部分凸包为例:
    – 最初,将最靠近左侧两点放入集合中,依次对剩余的点进行处理,待确认后逐个加入到集合中。处理过程为,假设A,B为初始点,下一点为C,若C位于AB的右侧,则直接添加到集合中,若C位于AB左侧,则将B弹出,将C放入集合中,如图找到ACDEF,当前点为G,显然G在EF的左侧,F弹出,G在DE的左侧,E弹出,G在CD的右侧,则G添加到集合中,形成ACDG,如果当前集合个数为1时,则直接入集合中,如B,如C(将B弹出后,集合个数为1),直到遍历完最后一个元素,算法停止
    凸包的下半部分,也类似,只需要判断相反的方向即可

三、算法实现

  • 1、生成随机数,保存到randText.txt文件中,并读入存入vector中
void RandDataLists()
{
	srand((unsigned)time(NULL));
	int Y = 100;
	int X = 0;
	int temps[100];
	int i = 0;
	int temp;
	while (i < 100)
	{
		temp = rand() % (Y - X + 1) + X;
		if (temp > 100 || temp < 0)
		{
			cout << "error" << endl;
		}
		temps[i] = temp;
		i++;
	}
	using namespace std;
	ofstream fout("randText.txt");
	cout << "i=" << i << endl;
	for (i = 0; i < 100; i++)
	{
		cout << temps[i] << " ";
		fout << temps[i] << endl;
	}
}

void GetRandPoints(vector<IntPoint>&ls)
{
	vector<IntPoint>&pLists=ls;
	using namespace std;
	ifstream fin("randText.txt");
	for (int i = 0; i < 50; i++)
	{
		int x, y;
		fin >> x >> y;
		pLists.push_back(IntPoint(x, y));
		//cout << "(" << x << "," << y << ")" << endl;
	}
}
  • 2、随机点根据x轴排序
void sortLists(set<IntPoint, comp>&ps)
{
	vector<IntPoint>lists;
	GetRandPoints(lists);
	set<IntPoint, comp>&setPoints=ps;
	for (auto item : lists)
	{
		setPoints.insert(item);
	}
}
  • 遍历获取凸包上半部分点
void UpHullSet(set<IntPoint, comp>& setPoints, stack<IntPoint>&hullStack)
{
	for (auto item : setPoints)
	{
		if (hullStack.size() < 2)
		{
			hullStack.push(item);
			continue;
		}
		while (true)
		{
			IntPoint pre, next;
			next = hullStack.top();
			hullStack.pop();
			pre = hullStack.top();
			IntPoint AB = next - pre;
			IntPoint AC = item - pre;
			if ((AB CROSS AC) > 0)
			{
				if (hullStack.size() == 1)
				{
					hullStack.push(item);
					break;
				}
			}
			else
			{
				hullStack.push(next);
				hullStack.push(item);
				break;
			}
		}
	}
}
  • 遍历获取凸包下半部分点
void LowHullSet(set<IntPoint,comp>& setPoints,stack<IntPoint>&hullStack)
{
	for (auto item : setPoints)
	{
		if (hullStack.size() < 2)
		{
			hullStack.push(item);
			continue;
		}
		while (true)
		{
			IntPoint pre, next;
			next = hullStack.top();
			hullStack.pop();
			pre = hullStack.top();
			IntPoint AB = next - pre;
			IntPoint AC = item - pre;
			if ((AB CROSS AC) < 0)
			{
				if (hullStack.size() == 1)
				{
					hullStack.push(item);
					break;
				}
			}
			else
			{
				hullStack.push(next);
				hullStack.push(item);
				break;
			}
		}
	}
}
  • 客户端代码
int main()
{
	//offsetTest();
	//RandDataLists();
	set<IntPoint, comp>setPoints;
	sortLists(setPoints);
	stack<IntPoint>upStack, lowStack;

	UpHullSet(setPoints, upStack);
	LowHullSet(setPoints,lowStack);

	getchar();
	return 0;
}

四、总结

算法过程相对比较简单,在2D空间效果良好,但在3D空间中有短板,计算机图形学中经常用到常规的算法,通过实现这些算法,也给自己打下基础,fighting!

发布了83 篇原创文章 · 获赞 24 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/LittleLittleFish_xyg/article/details/88038484