琢磨了幾天,終於將二分法凸包生成搞定了。
下圖是自己隨便繪製的線段,通過這些線段的節點從而計算出一個凸包。
這個圖是利用二分法生成的凸包。
下面將生成凸包的原理以及代碼進行介紹:
/* @接口 生成凸包
* @參數 Point2dArrray 點集
* @返回 Point2dArrray 凸包上的點
* @郵箱 [email protected]
* @時間 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
* @郵箱 [email protected]
* @時間 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 找到的點
* @郵箱 [email protected]
* @時間 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
* @郵箱 [email protected]
* @時間 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的順時針方向
* @郵箱 [email protected]
* @時間 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;
}