凸包(Convex Hull)是一个计算几何(图形学)中的概念,简单的来说就是对于一个给定的点集,我们需要求得一个凸多边形把给定的点集全部包含起来。
先附上我的结果:
如上图可以看到用绿色的线连成的一个凸多边形,就是一个凸包,它包含点集中所有的点。
一、算法流程
1、求出点集中满足min(x-y)、min(x+y)、max(x-y)、max(x+y)的四个点,并按逆时针(或者顺时针)方向组成一个点的链表。这4个点是离散点中与包含离散点的外接矩形的4个角点最近的点。这4个点构成的多边形作为初始凸包。
2、对于每个凸包上的点I和他的后续点为J,计算矢量线段IJ右侧的所有点到IJ的距离(若顺时针链表则计算左侧的点),求出距离最大的点K。
3、若存在K,则将K插入I到I之后 、J之前, 并将K赋给J(即更新矢量线段IJ = IK)。
4、重复2、3步,直到点集中没有在线段IJ右侧的点为止。
5、将J赋给I,J取其后续点,重复2、3、4步。
6、当凸包中任意相邻两点连线的右侧不存在离散点时,结束点集凸包求取过程。
二、我的实现
void ConvexHull::generateHull(Vector<Point> pts)
{
List<Point> othpts;
List<Point> hullpts; //保存凸包点的位置
for(int i =0; i< pts.size(); i++)
{
othpts.push_back(pts[i]);
}
//求出如下四点:min(x-y)、min(x+y)、max(x-y)、max(x+y)并顺次放入一个数组,组成初始凸包,这里构建一个顺时针凸包
Sort::quickSort(pts,0,pts.size()-1,cmpXsubY);
hullpts.push_back(pts[0]);
hullpts.push_back(pts[pts.size()-1]);
othpts.removeOne(pts[0]);
othpts.removeOne(pts[pts.size()-1]);
Sort::quickSort(pts,0,pts.size()-1,cmpXandY);
if(!hullpts.contains(pts[0]))
{
hullpts.insert(1,pts[0]);
}
if(!hullpts.contains(pts[pts.size()-1]))
{
hullpts.push_back(pts[pts.size()-1]);
}
for( int i =0; i< hullpts.size();i++)
{
hullpts[i].print();
}
othpts.removeOne(pts[0]);
othpts.removeOne(pts[pts.size()-1]);
int i = 0;
while(i<hullpts.size()) //遍历全部凸包
{
Line cline;//取凸包的一条线
if(i == hullpts.size() -1 ) //最后一个点,连接第一个点
{
cline.init(hullpts[i],hullpts[0]);
}else
{
cline.init(hullpts[i],hullpts[i+1]);
}
float maxdis = 0;
bool over = true;
int maxdisIndex = 0;
for( int m = 0; m< othpts.size(); m++)
{
if(IsRightPoint(othpts[m],cline)) //这里注意,本应该是在左侧寻找,但是我使用的屏幕中的坐标系(y轴朝下),因此要做一个转换
{
float dist = PointToLine(othpts[m],cline); //点到直线的距离
if(dist > maxdis)
{
maxdis = dist;
maxdisIndex = m;
over =false; //有外侧点,就不结束,
}
}
}
if(over == true) //结束,下次迭代则去找凸包的下一个线
{
i++;
}else{// 不结束,下次迭代仍然在i点开始,不过下一个点会更新成外侧距离最大点位置
hullpts.insert(i+1,othpts[maxdisIndex]);
over = true;
}
}
}