凸包生成(二分法)

琢磨了幾天,終於將二分法凸包生成搞定了。

下圖是自己隨便繪製的線段,通過這些線段的節點從而計算出一個凸包。

這個圖是利用二分法生成的凸包。 

下面將生成凸包的原理以及代碼進行介紹:


    /* @接口 生成凸包
     * @參數 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;
}
 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章