梁友棟-Barsky裁剪算法


梁友棟-Barsky裁剪算法

  Cyrus和Beck用參數化方法提出了比Cohen-Sutherland更有效的算法。后來梁友棟和Barsky獨立地提出了更快的參數化線段裁剪算法,也稱為Liany-Barsky(LB)算法。

 

一、梁友棟-Barsky裁剪算法思想:

  我們知道,一條兩端點為P1(x1,y1)、P2(x2,y2)的線段可以用參數方程形式表示: 

 

 

x= x1+ u·(x2-x1)= x1+ u·Δx

 y= y1+ u·(y2-y1)= y1+ u·Δy 

0≤u≤1

(1)

 

 

 

 

式中,Δx=x2-x1,Δy=y2-y1,參數u在0~1之間取值,P(x,y)代表了該線段上的一個點,其值由參數u確定,由公式可知,當u=0時,該點為P1(x1,y1),當u=1時,該點為P2(x2,y2)。如果點P(x,y)位於由坐標(xmin,ymin)和(xmax,ymax)所確定的窗口內,那么下式成立:

 

xmin≤x1+ u·Δx≤xmax

ymin≤y1+ u·Δy≤ymax

(2)

  

 

 

 

這四個不等式可以表示為:

u·pk ≤qk , k=1,2,3,4

(3)

  

 

 

其中,p、q定義為: 

 

p1=-Δx, q1=x1-xmin p2= Δx, q2=xmax-x1 p3=-Δy, q3=y1-ymin p4= Δy, q4=ymax-y1

(4)

  

 

 

從(4)式可以知道:任何平行於窗口某邊界的直線,其pk=0,k值對應於相應的邊界(k=1,2,3,4對應於左、右、下、上邊界)。如果還滿足qk<0,則線段完全在邊界外,應舍棄該線段。如果pk=0並且qk≥0,則線段平行於窗口某邊界並在窗口內,見圖中所示。公式(4)式還告訴我們:

  1、當pk<0時,線段從裁剪邊界延長線的外部延伸到內部;

     2、當pk>0時,線段從裁剪邊界延長線的內部延伸到外部;

例如,當Δx≥0時,對於左邊界p1<0(p1=-Δx),線段從左邊界的外部到內部;

           對於右邊界p2>0(p2=Δx),線段從右邊界的內部到外部。

     當Δy<0時,對於下邊界p3>0(p3=-Δy),線段從下邊界的內部到外部;

          對於上邊界p4<0(p4=Δy),線段從上邊界的外部到內部。

     當pK≠0時,可以計算出參數u的值,它對應於無限延伸的直線與延伸的窗口邊界k的交點,即:

 

u = qk/pk

(5)

  

 

 

對於每條直線,可以計算出參數u1和u2,該值定義了位於窗口內的線段部分:

  1、u1的值由線段從外到內遇到的矩形邊界所決定(pk<0),對這些邊界計算rk=qk/pk,u1取0和各個r值之中的最大值。

  2、u2的值由線段從內到外遇到的矩形邊界所決定(pk>0),對這些邊界計算rk=qk/pk,u2取0和各個r值之中的最小值。

  3、如果u1>u2,則線段完全落在裁剪窗口之外,應當被舍棄;否則,被裁剪線段的端點可以由u1和u2計算出來。

 

二、梁友棟-Barsky裁剪算法實現:

  1、初始化線段交點的參數:u1=0,u2=1;

  2、計算出各個裁剪邊界的p、q值;

  3、根據p、q來判斷:是舍棄線段還是改變交點的參數。

   (1) 當p<0時,參數r用於更新u1;      (u1=max{u1,…,rk})

   (2) 當p>0時,參數r用於更新u2。      (u2=min{u2,…,rk})

   (3)如果更新了u1或u2后,使u1>u2,則舍棄該線段。

   (4)當p=0且q<0時,因為線段平行於邊界並且位於邊界之外,則舍棄該線段。見下圖所示。

4、p、q的四個值經判斷后,如果該線段未被舍棄,則裁剪線段的端點坐標由參數u1和u2的值決定。

 

三、算法代碼

1.C++

bool _rtPruneLB(RtVector& vector, RtRect rect)
{
    RtVector dest;
    bool flag = false;
    float u1 = 0, u2 =1;
    int p[4], q[4];
    float r;
    p[0] = vector.sp.x - vector.ep.x;
    p[1] =   vector.ep.x - vector.sp.x;
    p[2] = vector.sp.y - vector.ep.y;
    p[3] = -vector.sp.y + vector.ep.y;

    q[0] = vector.sp.x - rect.x;
    q[1] = rect.x + rect.w - vector.sp.x;
    q[2] = vector.sp.y - rect.y;
    q[3] = rect.y + rect.h - vector.sp.y;
	
    for(int i = 0; i < 4; i++)
    {
        r = (float)q[i] / (float)p[i];
        if(p[i] < 0)
        {
            u1 = max(u1,r);
            if(u1 > u2)
            {
                flag = true;
            }
        }
        if(p[i] > 0)
        {
            u2 = min(u2, r);
            if(u1 > u2)
            {
                flag = true;
            }
        }
        if(p[i] == 0 && p[i] < 0)
        {
            flag = true;
        }
    }
	
	if ( flag )
	{
		return;
	}
    dest.sp.x = vector.sp.x - u1 *(vector.sp.x - vector.ep.x);
    dest.sp.y = vector.sp.y - u1 *(vector.sp.y - vector.ep.y);
    dest.ep.x = vector.sp.x - u2 *(vector.sp.x - vector.ep.x);
    dest.ep.y = vector.sp.y - u2 *(vector.sp.y - vector.ep.y);	
	vector =dest;
}

 

2.javascript

/*****************************************
* Liang - Barsky裁剪算法
*****************************************/
/**
驗證是否需要裁剪,計算斜率
@return boolean 是否裁減
*/
function ClipTest(p, q) {
    var flag = true;
    var r;
    if (p < 0.0) {
        r = q / p;
        if (r > u2) {
            flag = false;
        }
        else if (r > u1) {
            u1 = r;
            flag = true;
        }
    }
    else if (p > 0) {
        r = q / p;
        if (r < u1) {
            flag = false;
        }
        else if (r < u2) {
            u2 = r;
            flag = true;
        }
    }
    else if (q < 0) {
        flag = false;
    }
    return flag;
}

var u1, u2;
/**
根據矩形裁剪直線段
@param int wxl 左上角X坐標
@param int wxr 右下角X坐標
@param int wyt 右上角Y坐標
@param int wyb 右下角Y坐標
@param {X,Y} start 起點
@param {X,Y} stop 終點
@return  Sring 坐標字符串,如'2323,4343,5454,6563' 
*/
function ClipLine(wxl, wxr, wyt, wyb, start, stop) { 
    var dx, dy;
    u1 = 0;
    u2 = 1;
    dx = stop.X - start.X;
    if (ClipTest(-dx, start.X - wxl))
    {
        if (ClipTest(dx, wxr - start.X))
        {
            dy = stop.Y - start.Y;
            if (ClipTest(-dy, start.Y - wyt))
            {
                if (ClipTest(dy, wyb - start.Y))
                {
                    var arrCoords = [];
                    arrCoords[0] = parseInt(start.X) + parseInt(u1 * dx);
                    arrCoords[1] = parseInt(start.Y) + parseInt(u1 * dy);
                    arrCoords[2] = parseInt(start.X) + parseInt(u2 * dx);
                    arrCoords[3] = parseInt(start.Y) + parseInt(u2 * dy);
                    return arrCoords;
                }
            }
        }
    }
    return [];
}

/**
根據矩形裁剪折線
@param int wxl 左上角X坐標
@param int wxr 右下角X坐標
@param int wyt 右上角Y坐標
@param int wyb 右下角Y坐標
@param String coords 折線的坐標串(用,隔開)
@return Array 矩形內的折線段,如['2323,343,545,656,766,878','122,898,232,4545','778,4545,2321,1434']
*/
function ClipPolyLineByRect(wxl, wxr, wyt, wyb, coords) {
    var arrCoords = coords.split(',');
    var clipLines = [];
    for (var i = 0; i < (arrCoords.length - 2); i = i + 2) {
        var temp = ClipLine(wxl, wxr, wyt, wyb, { X: arrCoords[i], Y: arrCoords[i + 1] }, { X: arrCoords[i + 2], Y: arrCoords[i + 3] });
        if (temp.length > 0) {
            if (clipLines.length > 0) {
                //這里先拿上次最后一個點和現在第一個比較,如果相同,則他們屬於同一條線,將坐標追加到后面,否則存入新的線
                var lastCoords = clipLines[clipLines.length - 1];
                var re = new RegExp(temp[0]+','+temp[1]+'$', 'i');
                if (re.test(lastCoords)) {
                    clipLines[clipLines.length - 1] = lastCoords + ',' + temp[2] + ',' + temp[3];
                }
                else {
                    clipLines[clipLines.length] = temp.join(',');
                }
            } else {
                clipLines[clipLines.length] = temp.join(',');
            }
        }
    }
    return clipLines;
}

/**
根據當前屏幕裁剪折線
@param String coords 折線的坐標串(用,隔開)
@return Array 裁減后的多條線段
*/
function ClipPolyLineByCurrentRegion(coords) {
    var p1, p2;
    p1 = { X: GetEGISPosXFromWin(0), Y: GetEGISPosYFromWin(0) };
    p2 = { X: GetEGISPosXFromWin(MapWidth), Y: GetEGISPosYFromWin(MapHeight) };
    return ClipPolyLineByRect(p1.X, p2.X, p1.Y, p2.Y, coords);    
}

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM