Sutherland-Hodgman演算法
Sutherland-Hodgman演算法也叫逐邊裁剪法,該演算法是薩瑟蘭德(I.E.Sutherland)和霍德曼(Hodgman)在1974年提出的。這種演算法採用了分割處理、逐邊裁剪的方法。
一,基本思想:
一次用視窗的一條邊裁剪多邊形。
考慮視窗的一條邊以及延長線構成的裁剪線該線把平面分成兩個部分:可見一側;不可見一側。多邊形的各條邊的兩端點S、P。它們與裁剪線的位置關係只有四種
情況(1)僅輸出1個頂點P;
情況(2)輸出0個頂點;
情況(3)輸出線段SP與裁剪線的1個交點I;
情況(4)輸出線段SP與裁剪線的1個交點I和1個終點P
二、演算法實現:
1、已知:多邊形頂點數組src,頂點個數n,
定義新多邊形頂點數組dest。
2、賦初值:用變數flag來標識:
0表示在內側,1表示在外側。
3、對多邊形的n條邊進行處理,對當前點號的考慮為:0~n-1。
for(i=0;i<n;i++)
{
if(當前第i個頂點是否在邊界內側?)
{
if(flag!=0) /*前一個點在外側嗎?*/
{
flag=0;/*從外到內的情況,將標誌置0,作為下一次迴圈的前一點標誌*/
(dest + j) =求出交點; /*將交點dest放入新多邊形*/
j++;
}
(dest + j)= (src + i); /*將當前點srci放入新多邊形*/
j++;
}
else
{
if(flag==0) /*前一個點在內側嗎?*/
{
flag=1;/*從內到外的情況,將標誌置1,作為下一次迴圈的前一點標誌*/
(dest + j) =求出交點; /*將交點dest放入新多邊形*/
j++;
}
}
s= (src + i); /*將當前點作為下次迴圈的前一點*/
}
三,演算法特點:
Sutherland-Hodgeman多邊形裁剪演算法具有一般性,被裁剪多邊形可以是任意凸多邊形或凹多邊形,裁剪視窗不局限於矩形,可以是任意凸多邊形。
上面的演算法是多邊形相對視窗的一條邊界進行裁剪的實現,對於視窗的每一條邊界依次調用該演算法程式,並將前一次裁剪的結果多邊形作為下一次裁剪時的被裁剪多邊形,即可得到完整的多邊形裁剪程式。
view plaincopy to clipboardprint?
- //點在有向線段那側
- /*
- 向量叉積法
-
- 為簡單計,測試點表示為P點。假設視窗邊界方向為順時針,中所示,對於其中任一邊界向量,從向量起點A向終點B看過去:
-
- 如果被測試點P在該邊界線右邊(即內側),AB×AP的方向與X-Y平面垂直並指向螢幕裡面,即右手座標系中Z軸的負方向。
-
- 反過來,如果P在該邊界線的左邊(即外側),這時AB×AP的方向與X-Y平面垂直並指向螢幕外面,即右手座標系中Z軸的正方向。
-
- 設:點P(x,y)、點A(xA,yA)、點B(xB,yB),
- 向量AB={(xB-xA),(yB-yA)},
- 向量AP={(x-xA),(y-yA)},
-
- 那麼AB×AP的方向可由下式的符號來確定:
-
- V=(xB-xA)·(y-yA)-(x-xA)·(yB-yA) (3-14)
-
- 因此,當V≤0時,P在邊界線內側;
-
- 而V>0時,P在邊界線外側。
-
- */
- static int _RtInSide(RtVector vector, RtPoint point)
- {
- return (vector.ep.x - vector.sp.x) * (point.y - vector.sp.y) - (vector.ep.y - vector.sp.y) * (point.x - vector.sp.x);
- }
-
- //多邊形點必須是順時針方向
- int rtPrunePSH(RtPoint* src, int num, RtPoint** dest, int* total)
- {
- int i = 0, j = 0, k = -1, flag = 0;
- RtPoint start, stop;//被剪裁多邊形
- RtPoint sp, ep;//剪裁視窗
- RtPoint* temp = NULL;
- temp = (RtPoint*)malloc(sizeof(RtPoint) * 3 * (*total));
- if (temp == NULL) return -1;
-
-
- sp = *(src + num - 1);
-
- for ( i = 0; i < num; i++)//剪裁視窗
- {
- ep = *(src + i);
-
- start = *((*dest) + *total - 1);
- flag = _RtInSide(rtVector(sp, ep), start) > 0 ? 0 : 1;
-
- for ( j = 0; j < *total; j++)//被剪裁的多邊形
- {
- stop = *((*dest) + j);
- if (_RtInSide(rtVector(sp, ep), stop) <= 0)//當前第i個頂點是否在邊界內側
- {
- if (flag == 0)/*前一個點在外側嗎?*/
- {
- flag = 1;/*從外到內的情況,將標誌置0,作為下一次迴圈的前一點標誌*/
- k++;
- CRtPoint<double> point;
- CRtPoint<int> st(sp.x, sp.y), et(ep.x, ep.y);
- CRtLine<int> v1(st, et);
- st.SetData(start.x, start.y);
- et.SetData(stop.x, stop.y);
- CRtLine<int> v2(st, et);
- v2.Intersect(v1,point);
- (temp + k)->x = point[0];/*將交點放入新多邊形*/
- (temp + k)->y = point[1];
- }
- k++;
- *(temp + k) = stop;/*將當前點pi放入新多邊形*/
- }
- else
- {
- if (0 != flag)/*前一個點在內側嗎?*/
- {
- flag = 0;/*從內到外的情況,將標誌置1,作為下一次迴圈的前一點標誌*/
- k++;
- CRtPoint<double> point;
- CRtPoint<int> st(sp.x, sp.y), et(ep.x, ep.y);
- CRtLine<int> v1(st, et);
- st.SetData(start.x, start.y);
- et.SetData(stop.x, stop.y);
- CRtLine<int> v2(st, et);
- v2.Intersect(v1,point);
- (temp + k)->x = point[0];/*將交點放入新多邊形*/
- (temp + k)->y = point[1];
- }
- }
- start = stop;/*將當前點作為下次迴圈的前一點*/
- }
-
- sp = ep;
-
- *total = k + 1;
- memcpy(*dest, temp, sizeof(RtPoint) * (*total));
- k = -1;
- }
-
-
- return 0;
- }