多边形裁剪二:Weiler-Atherton算法

图片剪裁 专栏收录该内容
6 篇文章 0 订阅

Weiler-Atherton任意多边形裁剪

  Sutherland-Hodgeman算法解决了裁剪窗口为凸多边形窗口的问题,但一些应用需要涉及任意多边形窗口(含凹多边形窗口)的裁剪。Weiler-Atherton多边形裁剪算法正是满足这种要求的算法。

 

WeilerAtherton任意多边形裁剪算法描述:

  在算法中,裁剪窗口、被裁剪多边形可以是任意多边形:凸的、凹的(内角大于180o)、甚至是带有内环的(子区),见下图。

  裁剪窗口和被裁剪多边形处于完全对等的地位,这里我们称:

  1、被裁剪多边形为主多边形,记为A;

  2、裁剪窗口为裁剪多边形,记为B。

  主多边形A和裁剪多边形B的边界将整个二维平面分成了四个区域:
  1、A∩B(交:属于A且属于B); 
  2、A-B(差:属于A不属于B);
  3、B-A(差:属于B不属于A);
  4、A∪B(并:属于A或属于B,取反;即:不属于A且不属于B)。

  内裁剪即通常意义上的裁剪,取图元位于窗口之内的部分,结果为A∩B。

  外裁剪取图元位于窗口之外的部分,结果为A-B。

  观察下图不难发现裁剪结果区域的边界由被裁剪多 边形的部分边界和裁剪窗口的部分边界两部分构成,并且在交点处边界发生交替,即由被裁剪多边形的边界转至裁剪窗口的边界,或者反之。由于多边形构成一个封闭的区域,所以,如果被裁剪多边形和裁剪窗口有交点,则交点成对出现。这些交点分成两类:

  一类称“入”点,即被裁剪多边形由此点进入裁剪窗口,如图中a、c、e;
  一类称“出”点,即被裁剪多边形由此点离开裁剪窗口,如图中b、d、f。

                 

 

WeilerAtherton任意多边形裁剪算法思想:

  假设被裁剪多边形和裁剪窗口的顶点序列都按顺时针方向排列。当两个多边形相交时,交点必然成对出现,其中一个是从被裁剪多边形进入裁剪窗口的交点,称为“入点”,另一个是从被裁剪多边形离开裁剪窗口的交点,称为“出点”。

  算法从被裁剪多边形的一个入点开始,碰到入点,沿着被裁剪多边形按顺时针方向搜集顶点序列;

  而当遇到出点时,则沿着裁剪窗口按顺时针方向搜集顶点序列。

  按上述规则,如此交替地沿着两个多边形的边线行进,直到回到起始点。这时,收集到的全部顶点序列就是裁剪所得的一个多边形。

  由于可能存在分裂的多边形,因此算法要考虑:将搜集过的入点的入点记号删去,以免重复跟踪。将所有的入点搜集完毕后算法结束。

 

三、WeilerAtherton任意多边形裁剪算法步骤:

  1、顺时针输入被裁剪多边形顶点序列Ⅰ放入数组1中。

  2、顺时针输入裁剪窗口顶点序列Ⅱ放入数组2中。

  3、求出被裁剪多边形和裁剪窗口相交的所有交点,并给每个交点打上“入”、“出”标记。

    然后将交点按顺序插入序列Ⅰ得到新的顶点序列Ⅲ,并放入数组3中;

    同样也将交点按顺序插入序列Ⅱ得到新的顶点序列Ⅳ,放入数组4中;

  4、初始化输出数组Q,令数组Q为空。接着从数组3中寻找“入”点。

    如果“入”点没找到,程序结束。

  5、如果找到“入”点,则将“入”点放入S中暂存。

  6、将“入”点录入到输出数组Q中。并从数组3中将该“入”点的“入”点标记删去。

  7、沿数组3顺序取顶点:

    如果顶点不是“出点”,则将顶点录入到输出数组Q中,流程转第7步。
    否则,流程转第8步。

  8、沿数组4顺序取顶点:

    如果顶点不是“入点”,则将顶点录入到输出数组Q中,流程转第8步。
    否则,流程转第9步。

  9、如果顶点不等于起始点S,流程转第6步,继续跟踪数组3。

    否则,将数组Q输出;

    流程转第4步,寻找可能存在的分裂多边形。

    算法在第4步:满足“入”点没找到的条件时,算法结束。算法的生成过程见下图所示。

 

 

四、WeilerAtherton任意多边形裁剪算法特点:

  1、裁剪窗口可以是矩形、任意凸多边形、任意凹多边形。

  2、可实现被裁剪多边形相对裁剪窗口的内裁或外裁,即保留窗口内的图形或保留窗口外的图形,因此在三维消隐中可以用来处理物体表面间的相互遮挡关系。

  3、裁剪思想新颖,方法简洁,裁剪一次完成,与裁剪窗口的边数无关。

 

五、WeilerAtherton任意多边形裁剪算法小结:

  前面介绍的是内裁算法,即保留裁剪窗口内的图形。而外裁算法(保留裁剪窗口外的图形)同内裁算法差不多。

  外裁算法与内裁算法不同的是:

  1、从被裁剪多边形的一个“出点”开始,碰到出点,沿着被裁剪多边形按顺时针方向搜集顶点序列;

  2、而当遇到“入点”时,则沿着裁剪窗口按逆时针方向搜集顶点序列。

  按上述规则,如此交替地沿着两个多边形的边线行进,直到回到起始点为止。这时,收集到的全部顶点序列就是裁剪所得的一个多边形。

  由于可能存在分裂的多边形,因此算法要考虑:将搜集过的“出点”的出点记号删去,以免重复跟踪。将所有的出点搜集完毕后算法结束。

  Weiler-Atherton算法的的设计思想很巧妙,裁剪是一次完成,不象Sutherland-Hodgman多边形裁剪算法,每次只对裁剪窗口的一条边界及其延长线进行裁剪,如裁剪窗口有n条边,则要调用n次S-H算法后才能最后得出裁剪结果。

  但Weiler-Atherton算法的编程实现比Sutherland-Hodgman算法稍难,主要难在入、出点的查寻以及跨数组搜索上。

 

六、未测试的代码(正确代码见以后更新)

[cpp]  view plain copy
  1. typedef struct PWAPoint  
  2. {  
  3.     RtPoint point;  
  4.     int  flag;//0表示被剪裁多边形,1表示交点  
  5.     int  index;//属于被剪裁哪个线段的交点  
  6.     int index0;//属于剪裁哪个线段的交点  
  7.     int  flag0;//0表示入,1表示出,-1表示清除  
  8. }PWAPoint;  
  9.   
  10. typedef struct PWAArray  
  11. {  
  12.     double dist;//交点于启点的距离  
  13.     int index;//交点存储位置  
  14. }PWAArray;  
  15.   
  16. //多边形点必须是顺时针方向  
  17. int rtPrunePWA(RtPoint* src, int num, RtPoint* dest, int total)  
  18. {  
  19.     int i = 0, j = 0, k = 0,n = 0,m = 0, w = 0,u = 0;  
  20.     RtPoint sp, ep;//剪裁多边形  
  21.     RtPoint startP, endP;//被剪裁多边形  
  22.   
  23.     CRtPoint<double> point;  
  24.     CRtPoint<int> st, et;                   
  25.     CRtLine<int> v1,v2;  
  26.   
  27.     PWAPoint* injectP = (PWAPoint*)malloc(sizeof(PWAPoint) * num * total);  
  28.     PWAPoint* temp = (PWAPoint*)malloc(sizeof(PWAPoint) * num * (total));//剪裁加交点插入  
  29.     PWAPoint* temp0 = (PWAPoint*)malloc(sizeof(PWAPoint) * num * (total));//被剪裁加交点插入  
  30.     PWAArray* inP = (PWAArray*)malloc(sizeof(PWAArray) * num * (total));  
  31.     RtPoint* dst = (RtPoint*)malloc(sizeof(RtPoint) * num * total);//输出数组  
  32.   
  33.     //求交点  
  34.     startP = *(dest + total - 1);  
  35.     for (j = 0; j < total; j++)//被剪裁多边形  
  36.     {  
  37.         endP = *(dest + j);  
  38.         st.SetData(startP.x, startP.y);  
  39.         et.SetData(endP.x, endP.y);  
  40.         v2.SetData(st, et);  
  41.   
  42.         sp = *(src + num - 1);  
  43.         for (i = 0; i < num; i++)//剪裁多边形  
  44.         {  
  45.             ep = *(src + i);  
  46.             st.SetData(sp.x, sp.y);  
  47.             et.SetData(ep.x, ep.y);  
  48.             v1.SetData(st, et);   
  49.               
  50.             if(v2.Intersect(v1,point))//求交点   
  51.             {  
  52.                 injectP[k].point.x = point[0];  
  53.                 injectP[k].point.y = point[1];  
  54.                 injectP[k].flag = 1;  
  55.                 injectP[k].index = j;  
  56.                 injectP[k].index0 = i;  
  57.             }  
  58.             sp = ep;  
  59.         }  
  60.         startP = endP;  
  61.     }  
  62.   
  63.     //剪裁多边形插入交点  
  64.     w = 0;  
  65.     for (i = 0; i < num; i++)  
  66.     {  
  67.         temp[w].point = *(src + i);  
  68.         temp[w].flag = 0;  
  69.         w++;  
  70.   
  71.         n = 0;  
  72.         for (j = 0; j < k;j++)  
  73.         {  
  74.             if (injectP[j].index0 == j)//属于剪裁当前线段的交点  
  75.             {  
  76.                 inP[n].dist = sqrt(pow(injectP[j].point.x - temp[w].point.x , 2) + pow(injectP[j].point.y - temp[w].point.y , 2));  
  77.                 inP[n].index = j;  
  78.                 n++;  
  79.             }  
  80.         }  
  81.   
  82.         for (j = 0; j<n; j++)  
  83.         {  
  84.             for (m = j+1;m<n;m++)  
  85.             {  
  86.                 PWAArray tempp;  
  87.                 if (inP[j].dist > inP[m].dist)  
  88.                 {  
  89.                     tempp = inP[j];  
  90.                     inP[j] = inP[m];  
  91.                     inP[m] = tempp;  
  92.                 }  
  93.             }  
  94.         }  
  95.   
  96.         for (j = 0; j < n;j++)  
  97.         {  
  98.             temp[w] = injectP[inP[j].index];  
  99.             w++;  
  100.         }  
  101.   
  102.     }  
  103.   
  104.     //被剪裁多边形插入交点  
  105.     u = 0;  
  106.     for (i = 0; i < total; i++)  
  107.     {  
  108.         temp0[u].point = *((dest) + i);  
  109.         temp0[u].flag = 0;  
  110.         u++;  
  111.           
  112.         n = 0;  
  113.         for (j = 0; j < k;j++)  
  114.         {  
  115.             if (injectP[j].index0 == j)//属于剪裁当前线段的交点  
  116.             {  
  117.                 inP[n].dist = sqrt(pow(injectP[j].point.x - temp0[u].point.x , 2) + pow(injectP[j].point.y - temp0[u].point.y , 2));  
  118.                 inP[n].index = j;  
  119.                 n++;  
  120.             }  
  121.         }  
  122.           
  123.         for (j = 0; j<n; j++)  
  124.         {  
  125.             for (m = j+1;m<n;m++)  
  126.             {  
  127.                 PWAArray tempp;  
  128.                 if (inP[j].dist > inP[m].dist)  
  129.                 {  
  130.                     tempp = inP[j];  
  131.                     inP[j] = inP[m];  
  132.                     inP[m] = tempp;  
  133.                 }  
  134.             }  
  135.         }  
  136.           
  137.         for (j = 0; j < n;j++)  
  138.         {  
  139.             temp0[u] = injectP[inP[j].index];  
  140.             u++;  
  141.         }  
  142.           
  143.     }  
  144.   
  145.     //标记出入点  
  146.     for (i = 0; i < w; i++)  
  147.     {  
  148.         if (temp[i].flag == 1)  
  149.         {  
  150.             if (i == w - 1)  
  151.             {  
  152.                 if(temp[0].flag == 0)  
  153.                 {  
  154.                     temp[i].flag0 = 0;  
  155.                 }  
  156.                 if (temp[0].flag == 1)  
  157.                 {  
  158.                     temp[i].flag0 = 1;  
  159.                 }  
  160.             }  
  161.             else  
  162.             {  
  163.                 if (temp[i + 1].flag == 0)  
  164.                 {  
  165.                     temp[i].flag0 = 0;  
  166.                 }  
  167.                 else  
  168.                 {  
  169.                     temp[i].flag0 = 1;  
  170.                 }  
  171.             }             
  172.         }  
  173.     }  
  174.     for (i = 0; i < u; i++)  
  175.     {  
  176.         if (temp0[i].flag == 1)  
  177.         {  
  178.             if (i == u - 1)  
  179.             {  
  180.                 if(temp0[0].flag == 0)  
  181.                 {  
  182.                     temp0[i].flag0 = 0;  
  183.                 }  
  184.                 if (temp0[0].flag == 1)  
  185.                 {  
  186.                     temp0[i].flag0 = 1;  
  187.                 }  
  188.             }  
  189.             else  
  190.             {  
  191.                 if (temp0[i + 1].flag == 0)  
  192.                 {  
  193.                     temp0[i].flag0 = 0;  
  194.                 }  
  195.                 else  
  196.                 {  
  197.                     temp0[i].flag0 = 1;  
  198.                 }  
  199.             }             
  200.         }  
  201.     }  
  202.   
  203.     k = 0;  
  204.     //统计剪裁区域  
  205. loop3:  
  206.     for (i = 0; i < u; i++)//被剪裁区域  
  207.     {  
  208.         if (0 == temp0[i].flag0)//是入点  
  209.         {  
  210.             dst[k] = temp0[i].point;  
  211.             k++;  
  212.             temp0[i].flag0 = -1;  
  213.             goto loop0;  
  214.         }  
  215.     }  
  216.     return 1;  
  217.   
  218. loop0:  
  219.     for (j = i; j < u; j++)  
  220.     {  
  221.         if (temp0[i].flag0 != 1)//不是出点  
  222.         {  
  223.             dst[k] = temp0[i].point;  
  224.             temp0[i].flag0 = -1;  
  225.             k++;  
  226.         }  
  227.         else  
  228.         {  
  229.             goto loop1;  
  230.         }  
  231.     }  
  232.   
  233. loop1:  
  234.     for (m = 0; m < w; m++)  
  235.     {  
  236.         if (dst[k].x == temp[m].point.x && dst[k].y == temp[m].point.y)  
  237.         {  
  238.             goto loop2;  
  239.         }  
  240.     }  
  241.   
  242. loop2:  
  243.     for (n = m+1;n < w; n++)  
  244.     {  
  245.         if (temp[i].flag0 != 0)//不是入点  
  246.         {  
  247.             dst[k] = temp[i].point;  
  248.             temp[i].flag0 = -1;  
  249.             k++;  
  250.         }  
  251.         else  
  252.         {  
  253.             goto loop3;  
  254.         }  
  255.     }  
  256.   
  257.     free(injectP);  
  258.     free(temp);  
  259.     free(temp0);  
  260.     free(inP);  
  261.     return 0;  
  262. }  

 

  • 9
    点赞
  • 3
    评论
  • 33
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值