裁剪做用:
選擇顯示的內容--圖形在窗口內的部分被顯示出來,窗口外的部分被裁剪掉
圖形中每一個圖形基本元素都要通過裁剪,所以裁剪直接影響整個圖形系統的效率。html
裁剪窗口:矩形,凸多邊形,任意多邊形
裁剪類型:二維裁剪、三維裁剪
裁剪對象:直線段、多邊形、文字等
裁剪方法:
直線的裁剪方法: Sutherland-Cohen算法 , Cyrus-Beck算法,梁友棟-Barsky算法
多邊形的裁剪方法:Sutherland-Hodgman算法
三維的裁剪方法: Sutherland-Cohen算法 ,梁友棟-Barsky算法
算法
本算法又稱爲編碼裁剪算法編程
Sutherland–Cohen算法分紅兩部分:測試
第一步,斷定:
1) 徹底在窗口內的直線段,稱爲徹底可見的線段,如AB。保留着
2) 徹底在窗口外的線段,稱爲徹底不可見線段,如CD。拋棄掉編碼
第二步,處理不能判定爲徹底可見或徹底不可見的線段,如IJ、KL
*這時須要計算出直線段和窗口邊界的一個交點,這個交點把直線分紅兩段,其中一條爲徹底不可見的線段,被拋棄。
*對餘下部分再做第一步的判斷,重複上述過程,直到直線段餘下的部分可用第一步的判斷得出確定的結論爲止。
spa
爲使計算機可以快速判斷一條直線段與窗口屬何種關係,採用以下編碼方法。窗口的四條邊把整個平面分紅九個區域,每個區域採用四位編碼表示:3d
在x=xL左側的區域,編碼的第四位是1;
在x=xR右側的區域,編碼的第三位是1;
在y=yB下側的區域,編碼的第二位是1;
在y=yT上側的區域,編碼的第一位是1。code
如何判斷?
對要被裁剪的線段的兩個端點進行區域編碼。htm
若是其所在的區域的編碼均是0000(相與),則這條線段徹底可見;
若是兩個編碼的邏輯與不爲0000,則這條線段徹底不可見對象
線段KL爲例,從K點(1001)的編碼分析出K在x=xL的左側,KL必和x=xL有交點,求出其交點M,KM顯然是徹底不可見的,於是只要對ML從第一步開始重複上述處理步驟。
因爲ML仍是不能用第一步下結論,又從M的編碼發現M在y=yT的上側,於是要求ML和y=yT的交點N。
丟掉MN,對NL用第一步的方法可判定NL爲徹底可見,至此裁剪結束。
float xl, xr, yt, yb; unsigned char code(float x, float y) { unsigned char c = 0; if (x < xl) c = c|1; //按位或 else if (x > xr) c = c|2; if (y < yb) c = c|4; else if (y > yt) c = c|8; return c; }//給九個區域編碼 void clip(float x0, float y0, float x2, float y2) { unsigned char c1, c2, c; float x, y, wx, wy; c1 = code(x0, y0); c2 = code(x2, y2); while ((!(c1 == 0)) || (!(c2 == 0))) { if ((c1& c2)) return; //兩端點邏輯與不爲0,則在區域外,裁去 c = c1; if (c == 0) c = c2; wx=x2-x0; wy=y2-y0; if ((c & 1) == 1) { y = y0 + wy * (xl - x0) /wx; x = xl; }//端點在xl左側,求與xl的交點 else if ((c & 2) == 2) { y = y0 +wy * (xr - x0) /wx; x = xr; } //端點在xr右側,求與xr的交點 else if ((c & 4) == 4) { x = x0 +wx * (yb - y0) /wy; y = yb; } //端點在yb下方,求與yb的交點 else if ((c & 8) == 8) { x = x0 +wx * (yt - y0) / wy; y = yt; } //端點在yt上方,求與yt的交點 if (c == c1) { x0 = x; y0 = y; c1 = code(x0, y0); } else { x2 = x; y2 = y; c2 = code(x2, y2); } //用交點代替端點,再返回第一步 }// While() glLine(int(x0), int(y0), int(x2), int(y2)); }
Cohen-Sutherland裁剪算法對不與邊框相交的線段進行裁剪時效率較高,而對與窗口邊界有交點的線段裁剪效率低。
於是比較適合兩種狀況的裁剪:一是大部分線段徹底可見;二是大部分線段徹底不可見。
並且不少的時候,被裁剪線段僅與窗口邊界延長線相交,求交點到最後是無效的操做,由於線段可能徹底被丟棄;而且被裁剪線段與窗口邊界相交時交點的取得比較複雜。
好比像下圖這樣的裁剪,這條紅色線段徹底是在裁剪窗口的外部,卻須要進行算法計算,最後線段徹底被丟棄!
中點分隔算法是對Sutherland-Cohen算法在求交點方面的改進。
核心思想是經過二分逼近來肯定直線段與 窗口的交點。
取線段的中點
一、若中點不在窗口內, 則把中點和離窗口邊界最遠點構成的線段丟掉,以線段上的另外一點和該中點再構成線段求其中點
二、如中點在窗口內,則又以中點和最遠點構成線段, 並求其中點,直到中點與窗口邊界的 座標值在規定的偏差範圍內相等
重複上述過程,直到線段長度小於給定的小數ε爲止。
在顯示時ε可取成一個象素的寬度,對分辨率爲2N×2N的顯示器來講,上面講的二分的過程最多隻要做N次。
Cyrus-Beck算法能夠處理任意凸多邊形對線段的裁剪。
考慮如圖所示一個凸多邊形區域R和一條線段P1P2,要求計算線段落在區域R中的部分。
假定A是區域R邊界L上一點;N是區域邊界在A點的內法向量;線段P1P2用參數方程表示:
對於線段P1P2的參數方程表示,若是能判斷出線段進入多邊形時候的參數ts和線段退出多邊形時的參數te,則tste之間的線段爲裁剪完畢後的結果。
假定A爲區域邊界L上的任意一點,記L的內法向量(垂直)爲N對於線段上任意一點 Pi, Pi和多邊形邊界L的關係有三種可能(t 爲此點的參數值):
性質(1): 若是點P(t)在多邊形全部邊的內側,則稱P是在多邊形的內側。
可見線段的參數區間
double ts,te; int Cyrus_Beck (int k,double A[][2],double N[][2],double x[2],double y[2],double *ts,double *te) { int i,j; double t,dn,nw,D[2],W[2]; *ts=0; *te=1; for(i=0; i<k; i++) { dn=N[i][0]*(x[1]- x[0])+N[i][1]* (y[1]-y[0]); nw=N[i][0]* (x[0]-A[i][0])+N[i][1]* (y[0]- A[i][1]); if(fabs(dn)<1.0e-6) //平行 { if(nw<0) return 0; //p在L外側 } else { t=-nw/dn; if(dn<0) { if(t< *te) *te=t; //終點 } else if(t> *ts) *ts=t; //起點 } if(*ts>*te) return 0; //在區域外 } return 1; }
當凸多邊形是矩形窗口,且矩形的邊平行於座標軸時, Cyrus-Beck算法可簡化爲梁友棟-Barsky算法。
初始化線段在邊界內的端點參數爲ts=0、te=1。
計算出各個裁剪邊界的r、s值。
當r=0且s<0時,捨棄該線段;不然計算線段與邊界的交點參數t。
當r<0時,參數t用於更新ts;
當r>0時,參數t用於更新te。
若是更新了ts或te後,使ts>te,則捨棄該線段。
double ts,te; double xL,xR,yB,yT; bool visible=false; void Liang_Barsky (double x[2],double y[2],double *ts,double *te) { double dx,dy; visible=false; dx=x[1]-x[0]; dy= y[1]-y[0]; *ts=0; *te=1; if(clipt(-dx,x[0]-xL,ts,te)) if(clipt(dx,xR-x[0],ts,te)) if(clipt(-dy,y[0]-yB,ts,te)) if(clipt(dy,yT-y[0],ts,te)) visible=true; } bool clipt (double r,double s,double * ts,double *te) { double t; if(r<0) { t=s/r; if(t>* te) return false; else if(t>* ts) *ts=t; } //起點組 else if(r>0) { t=s/r; if(t<*ts) return false; else if(t<* te) *te=t; } //終點組 else if(s<0) return false; //r=0且s<0則徹底不可見 return true; }
梁友棟-Barsky算法比Sutherland-Cohen算法更有效,由於須要計算的交點數目減小了。更新參數僅僅須要一次除法,線段與窗口的交點只計算一次就計算出 ts、te的最後的值。而對於Sutherland-Cohen算法,即便一條線段徹底落在裁剪窗口以外,也要對其反覆求交點,並且每次求交都須要除法和乘法運算。
http://www.javashuo.com/article/p-plmvzbpp-et.html
該算法的基本思想是將多邊形邊界做爲一個總體,每次用窗口的一條邊對要裁剪的多邊形和中間結果多邊形進行裁剪,體現一種分而治之的思想
基本原理
只要對多邊形用窗口的四條邊依次裁剪四次即可獲得裁剪後的多邊形。
每次用窗口的一條邊界(包括延長線)對要裁剪的多邊形進行裁剪,裁剪時順序地測試多邊形各頂點,保留邊界內側的頂點,刪除外側的頂點,同時,適時地插入新的頂點(即交點和窗口頂點),從而獲得一個新的多邊形頂點序列。
而後以此新的頂點序列做爲輸入,相對第二條窗邊界線進行裁剪,又獲得一個更新的多邊形頂點序列。
依次下去,相對於第三條、第四條邊界線進行裁剪,最後輸出的多邊形頂點序列即爲所求的裁剪好了的多邊形。以下圖所示。
算法的輸入是以頂點序列表示的多邊形,輸出也是一個頂點序列,構成一個或多個多邊形。
考慮以窗口的一條邊以及延長線構成的裁剪線,該線把平面分紅兩部分:一部分包含窗口,稱爲可見側;另外一部分稱爲不可見側。
每條線段端點S、P與裁剪線比較後可輸出0至2個頂點。
S、P都在可見一側,則輸出P。
S、P都在不可見一側,則輸出0個頂點。
S在可見一側,P在不可見一側,則輸出SP與裁剪線的交點I。
S在不可見一側,P在可見一側,則輸出SP與裁剪線的交點I和P。
字符裁剪的策略有如下三種:
串精度裁剪
字符串徹底落入窗口以內時才顯示,不然不顯示,裁剪結果如圖(b)所示。其思想是求出字符串的包圍盒,比較包圍盒的邊界極值與窗口的邊界極值,若包圍盒徹底落於窗口以內,則顯示字符串,不然不顯示。
字符精度裁剪
當一個字符徹底包含於窗口內時,顯示該字符,不然不顯示。可先用字符串包圍盒判斷應該徹底、部分仍是不顯示字符串,對部分顯示的字符串,再用每一個字符的包圍盒判斷該字符是否是應該顯示。裁剪結果如圖(c)所示。
基於構成字符最小元素的裁剪
這種策略最爲精確,即便字符只有一部分在窗口內,也要把這一部分顯示出來。對點陣字符來講,構成字符的最小元素爲像素,此時字符的裁剪轉化爲點裁剪;對矢量字符來講,構成字符的最小元素是曲線段,字符的裁剪轉化爲曲線的裁剪。裁剪結果如圖(d)所示。