琢磨了几天,终于将二分法凸包生成搞定了。
下图是自己随便绘制的线段,通过这些线段的节点从而计算出一个凸包。
这个图是利用二分法生成的凸包。
下面将生成凸包的原理以及代码进行介绍:
/* @接口 生成凸包
* @参数 Point2dArrray 点集
* @返回 Point2dArrray 凸包上的点
* @邮箱 575814050@qq.com
* @时间 2019年5月29号
*/
Point2dArray convexHull(const Point2dArray &node)
{
Point2dArray temp = quickSort(node, true);
DoxPoint2d ptMin = temp.first();
DoxPoint2d ptMax = temp.last();
Point2dArray lNode, rNode, ret;
splitPonins(node, ptMin, ptMax, lNode, rNode);
ret.append(ptMin); ret.append(convexHull(lNode, ptMin, ptMax));
ret.append(ptMax); ret.append(convexHull(rNode, ptMax, ptMin));
return ret;
}
1、首先对传入的点集进行快速排序
2、然后取出最大点ptMax与最小的点ptMin,以这个两个点的连线作为分界线
3、利用向量叉乘原理,分别计算出在线段的左侧以及右侧的点集
4、将最小的点ptMin加到凸包点集中,然后计算左侧点集的凸包
5、将最大的点ptMax加到凸包点集中,然后计算右侧的点集凸包
/* @接口 计算点集凸包
* @参数 DoxPoint2d 分界线的起点
* @参数 DoxPoint2d 分界线的终点
* @返回 Point2dArrray 凸包上的点集
* @返回 bool 成功返回值为true,否则返回值为false
* @邮箱 575814050@qq.com
* @时间 2019年5月29号
*/
Point2dArray convexHull(const Point2dArray &node, const DoxPoint2d &spt, const DoxPoint2d &ept)
{
Point2dArray lNode, rNode, ret;
DoxPoint2d tpt = findPolePoint(node, spt, ept);
splitPonins(node, spt, tpt, lNode, rNode);
if(lNode.length() == 0) { ret.append(tpt); }
else ret.append(convexHull(lNode, spt, tpt));
ret.append(tpt); lNode.clear(); rNode.clear();
splitPonins(node, tpt, ept, lNode, rNode);
if(lNode.length() == 0) { ret.append(tpt);}
else ret.append(convexHull(lNode, tpt, ept));
return ret;
}
1、计算点集中离分界线最远的点tpt
2、将分界线的起点spt与tpt构成新的分界线
3、然后再点集按照新的分界线进行左右分开
4、如果左边点集中的点的个数为0,那么将最远的点tpt作为凸包上的点加到返回值的点集中,否则继续1-4步
5、将最远的点tpt与分界线终点ept构成新的分界线
6、然后再点集按照新的分界线进行左右分开
7、如果左边点集中的点的个数为0,那么将最远的点tpt作为凸包上的点加到返回值的点集中,否则继续1-6步
下面是一些辅助函数:
/* @接口 查找点集中里线段的极点(最远或最近)
* @参数 Point2dArray 点集
* @参数 DoxPoint2d 线段的起点
* @参数 DoxPoint2d 线段的终点
* @返回 DoxPoint2d 找到的点
* @邮箱 575814050@qq.com
* @时间 2019年5月30号
*/
DoxPoint2d findPolePoint(const Point2dArray &, const DoxPoint2d &, const DoxPoint2d &, bool Furthest = true);
{
double val = Furthest ? -9999 : 9999;
DoxPoint2d pole;
for(int idx = 0; idx < node.length(); ++idx)
{
DoxPoint2d pt = node[idx];
double temp = fabs(crossMultiply(spt, pt, ept));
if((Furthest && temp > val) || ((!Furthest) && temp < val))
{
pole = pt;
val = temp;
}
}
return pole;
}
注释:计算的原理是根据三个点构成的面积取最大的。
/* @接口 用两个点将点集进行划分
* @参数 DoxPoint2d 起点
* @参数 DoxPoint2d 终点
* @参数 Point2dArrray 站在起点,看着终点,左边所有的点
* @参数 Point2dArrray 站在起点,看着终点,右边所有的点
* @返回 bool 成功返回值为true,否则返回值为false
* @邮箱 575814050@qq.com
* @时间 2019年5月30号
*/
void splitPonins(const Point2dArray &node, const DoxPoint2d &spt, const DoxPoint2d &ept, Point2dArray &lNode, Point2dArray &rNode)
{
for(int idx = 0; idx < node.length(); ++idx)
{
DoxPoint2d pt = node[idx];
double val = crossMultiply(ept, spt, pt);
if(val > 0) lNode.append(pt);
else if(val < 0) rNode.append(pt);
}
}
/* @接口 计算出来是一个面积
* @参数
* @参数
* @参数
* @返回 大于0:ep在矢量opsp的逆时针方向;
等于0:opspep三点共线;
小于0:ep在矢量opsp的顺时针方向
* @邮箱 575814050@qq.com
* @时间 2019年5月30号
*/
double crossMultiply(const DoxPoint2d &spt, const DoxPoint2d &mpt, const DoxPoint2d &ept)
{
DoxPoint2d pt1(spt._x - mpt._x, spt._y - mpt._y);
DoxPoint2d pt2(ept._x - mpt._x, ept._y - mpt._y);
return crossMultiply(pt1, pt2);
}
double crossMultiply(const DoxPoint2d &spt, const DoxPoint2d &ept)
{
return spt._x * ept._y - spt._y * ept._x;
}