• 梁友栋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);    
    }
    

      

  • 相关阅读:
    [django]media_url
    django部署到apache出现DLL load failed

    Django model relationship field
    python下划线变量的含义
    JavaScript 对象属性作实参以及实参对象的callee属性
    Javascript变长参数和默认参数
    JavaScript 数组
    JavaScript 操作对象属性(设置属性, setter/getter, 序列化)
    JavaScript 对象笔记
  • 原文地址:https://www.cnblogs.com/jenry/p/2347983.html
Copyright © 2020-2023  润新知