现在的位置: 首页 > 综合 > 正文

计算点、线、面等元素之间的交点、交线、封闭区域面积和闭合集(续8)

2012年12月18日 ⁄ 综合 ⁄ 共 4262字 ⁄ 字号 评论关闭
 

现在介绍平面上顶点集合凸包的Bentley-Faust-Preparata (BFP)方法,与前面讲解的Graham ScanAndrew's Monotone Chain方法不同之处是,预先并不需要将整个顶点集合一起按照XY轴数值排序。这种方法虽然是种近似方法,但时间复杂度降低,对于大数据量顶点的处理要求比较适合。

基本方法是,取代整个顶点集合的依次XY轴排序,而首先依X轴数值大小划分几个区间,在每个区间中找到YminYmax顶点,以所有区间的这些顶点为预选,采用Andrew's Monotone Chain算法中使用过的“左转”方式来判断凸包条件是否成立,最后构成上下两个凸包子集,将其合并得到结果。其近似的原因是,每个区间可能不只有YminYmax等顶点作为预选。倘若将区间数量增大,则算法的准确性上升。

  
          参考代码:

//     输入: 没有排序的顶点数组P[],数组中的顶点个数n,准备将X轴分开成 k个区间

//     输出:  生成的凸包顶点集合H[]

//     返回的数值: 凸包H[]中的顶点个数

int CDEMAlgorithm::nearHull_2D( Point* P, int n, int k, Point* H )

{

    int minmin=0, minmax=0; //准备记录X轴数值最小情况下Y轴数值最小和最大顶点索引

    int maxmin=0, maxmax=0; //准备记录X轴数值最大情况下Y轴数值最小和最大顶点索引

    double xmin = P[0].xxmax = P[0].x;   //初始化X轴最小和最大数值

    Point* cP;                       // 被处理的顶点

    int    bot=0, top=(-1);          // 指示栈底和栈顶

    for (int i=1; i<n; i++) {            //对所有顶点循环处理

        cP = &P[i];

        if (cP->x <= xmin) {

            if (cP->x < xmin) {        // 有更小的X轴数值出现

                 xmin = cP->x;

                 minmin = minmax = i//记录下顶点索引

            }

            else {                      // 若相同的X轴最小数值出现

                 if (cP->y < P[minmin].y)

                     minmin = i;          //记录下顶点索引,此时Y轴更小

                 else if (cP->y > P[minmax].y)

                     minmax = i;          //记录下顶点索引,此时Y轴更大

            }

        }

        if (cP->x >= xmax) {             // 有更大或相同的X轴数值出现

            if (cP->x > xmax) {        // 有比xmax还大的顶点出现

                 xmax = cP->x;

                 maxmin = maxmax = i;     //记录顶点索引

            }

            else {                      //若相同的X轴最大数值出现

                 if (cP->y < P[maxmin].y)

                     maxmin = i;          //记录下顶点索引,此时Y轴更小

                 else if (cP->y > P[maxmax].y)

                     maxmax = i;          //记录下顶点索引,此时Y轴更大

            }

        }

    }

    if (xmin == xmax) { // 一种极端情况出现:X轴最小与最大数值相同,即所有数值等同

        H[++top] = P[minmin];           //记录下此顶点

        if (minmax != minmin)           // 如果Y轴的数值不同

            H[++top] = P[minmax];        //记录下此顶点

        return top+1;                   // 返回顶点个数,这种特殊情况下是1或2个

    }

   

Bin*   B = new Bin[k+2];   // 初始化区间数据结构

    B[0].min = minmin;         B[0].max = minmax;        // 设置第一个区间

    B[k+1].min = maxmin;       B[k+1].max = maxmax;      // 设置最后一个区间

    for (int b=1; b<=k; b++) {

        B[b].min = B[b].max = -99;   // 初始赋值

    }

    for (int b, i=0; i<n; i++) {     //循环处理每个顶点

        cP = &P[i];

        if (cP->x == xmin || cP->x == xmax) // 如果是整个集合中的X轴最小或最大数值

            continue;                    //继续下次循环

        if (isLeft( P[minmin], P[maxmin], *cP) < 0) { // 当前顶点处于低线之下

            b = (int)( k * (cP->x - xmin) / (xmax - xmin) ) + 1;//得Bin结构的序号

            if (B[b].min == -99)       // 这个Bin结构中还没有赋Y轴上的最低顶点

                 B[b].min = i;           //记录索引

            else if (cP->y < P[B[b].min].y //若比已经记录的最低数值还低

                 B[b].min = i;           // 新的最低顶点

            continue;

        }

        if (isLeft( P[minmax], P[maxmax], *cP) > 0) { //当前顶点处于高线之上

            b = (int)( k * (cP->x - xmin) / (xmax - xmin) ) + 1; //得Bin结构的序号

            if (B[b].max == -99)      // 这个Bin结构中还没有赋Y轴上的最高顶点

                 B[b].max = i;           //记录索引

            else if (cP->y > P[B[b].max].y) //若比已经记录的最高数值还高

                 B[b].max = i;           //新的最高顶点

            continue;

        }

    }

    //和前面讲解的Andrew's Monotone Chain算法中一样,使用 “左转”方式来判断凸包条件是否成立,分别构成上下两个子集

    for (int i=0; i <= k+1; ++i)//对每个区间循环处理

    {

        if (B[i].min == -99) // 如果区间中没有最低顶点记录的话,继续下次循环

            continue;

        cP = &P[ B[i].min ];   // 取出当前区间中的最低顶点

        while (top > 0)        // 当栈中至少有2个元素的时候,top > 0

        {

            // 看是否满足“左转”条件

            if (isLeft( H[top-1], H[top], *cP) > 0)

                 break;         // 此顶点是满足条件的新凸包顶点

            else

                 top--;         // 由于顶点破坏凸包条件,需要弹出栈顶元素,直到满足

        }

        H[++top] = *cP;        // 将这个顶点压入栈

    }

    //下面,计算上半部分的凸包顶点集合

    if (maxmax != maxmin)      //如果X轴数值最大情况下Y轴有不同顶点存在

        H[++top] = P[maxmax]; //将X轴数值与Y轴数值最大的顶点压入栈

    bot = top;                 //记住准备增加元素到栈前已经存在的元素个数

    for (int i=k; i >= 0; --i)   //对每个区间循环处理

    {

        if (B[i].max == -99) //如果区间中没有最高顶点记录的话,继续下次循环

            continue;

        cP = &P[ B[i].max ];   //取出当前区间中的最高顶点

        while (top > bot)      // top还是比开始记住的bot大,表明栈中至少有2个元素

        {

            // 看是否满足“左转”条件

            if (isLeft( H[top-1], H[top], *cP) > 0)

                 break;         //此顶点是满足条件的新凸包顶点

            else

                 top--;         //由于顶点破坏凸包条件,需要弹出栈顶元素,直到满足

        }

        H[++top] = *cP;        //将这个顶点压入栈

    }

    if (minmax != minmin)    //如果X轴数值最小情况下Y轴有不同顶点存在

        H[++top] = P[minmin]; //把这最后一个顶点压入栈

    delete B;                  // 释放

    return top+1;              //返回输出的凸包数组中的顶点个数
}

抱歉!评论已关闭.