直线裁剪算法研究
摘要:直线裁剪是计算机图形学中的一个重要技术,在对常见的直经线裁剪的算法分析的基础上,针对Cohen-Sutherland算法和Liang-Barsky算法进行了分析研究。并对两种算法了计算直线与窗口边界的交点时,进行了有效有比较。
关键词:裁剪;算法;Cohen-Sutherland;Liang-Barsky;
1 引言
直线是图形系统中使用最多的一个基本元素。所以对于直线段的裁剪算法是被研究最深入的一类算法,目前在矩形窗口的直线裁剪算法中,出现了许多有效的算法。其中比较著名的有:Cohen-Sutherland算法、中点分割算法、Liang-Ba rsky算法、Sobkow-Pospisil-Yang 算法,及Nicholl-Lee-Ncholl算法等。
2 直线裁剪的基本原理
图1所示的为直线与窗口边界之间可能出现的几种关系。可以通过检查直线的两个端点是否在窗口之确定如何对此直线裁剪。如果一直线的两个端点均在窗口边界之(如图1中P5到P6的直线),则此直线应保留。如果一条直线的一个端点在窗口外(如P9)另一个点在窗口(如P10),则应从直线与边界的交点(P9)处裁剪掉边界之外的线段。如果直线的两个端点均在边界外,则可分为两种情况:一种情况是该直线全部在窗口之外;另一种情况是直线穿过两个窗口边界。图中从P3到P4的直线属于前一种情况,应全部裁剪掉;从P7到P8的直线属于后一种情况,应保留P7到P8的线段,其余部分均裁剪掉。
图1直线相对干窗口边界的栽剪
直线裁剪算法应首先确定哪些直线全部保留或全部裁剪,剩下的即为部分裁剪的直线。对于部分裁剪的直线则首先要求出这些直线与窗口边界的交点,把从交点开始在边界外的部分裁剪掉。一个复杂的画面中可能包含有几千条直线,为了提高算法效率,加快裁剪速度,应当采用计算量较小的算法求直线与窗口边界的交点。
3 cohen-sutherland直线裁剪算法
Cohen-Sutherland算法的大意是:对于每条线段P1P2,分为3种情况处理。
①若P1P2完全在窗口,则显示该线段P1P2,简称“取”之。
②若P1P2明显在窗口外,则丢弃该线段,简称“弃”之。
③若线段既不满足“取”的条件,也不满足“弃”的条件,则把线段分为两段。其中一段完全在窗口外,可弃之。然后对另一段重复上述处理。
1.区域码及其建立
Cohen-Sutherland直线裁剪算法的核心是把所有直线的端点均分配一个表示其相对位置的4位二进制代码。此代码称为区域码。区域码按照端点与窗口边界的相对位置编码,即区域码的4位分别代表端点位于窗口的上、下、左、右。区域码从右到左的各位所代表的坐标区如下所示:
位 4 3 2 1
坐标区上下右左
上述各位中某位为1,则表示点位于此坐标区。窗口周围各坐标区的区域码如图2所示。由图2可见,位于窗中的点,其区域码应为0000,位于窗口左下方的点,其区域码应为0101,其余类推。
区域码各位的值可以通过对端点坐标(x,y)与窗口边界的比较求得。如果x ①计算出端点坐标与窗口边界的差。 ②按计算出的各个差的符号把区域码的相应位置为0或1,即 区域码的第一位置为(x-x wmin)的符号位; 区域码的第二位置为(x wmin-x)的符号位; 区域码的第三位置为(y-y wmin)的符号位; 图2 区域码区域码的第四位置为(y wmin-y)的符号位。 2.区域码裁剪算法 对所有直线的端点都建立了区域码之后,就可按区域码判断直线在窗口之或窗口之外。这可分为如下几种情况: ①若一直线的两个端点的区域码均为0000则此直线在窗口边界之,应子保留。 ②若一直线的两个端点的区域码的同一位同时为1,则此直线全部在窗口边界之外,应子裁剪。例如,若一直线的一个端点的区域码为1001,另一个端点的区域码为0101,则此两端点的区域码的第一位均为1,说明此两端点均在窗口边界之左,因此,直线在窗 口边界之外,应予裁剪。可用将直线两个端点的区域码进行与操作的方法,判断直线是否在窗口之外,若与操作的结果为0000则两端点的区域码任何位均不同时为1,此直线不一定被裁剪。 ③以上两种情况之外的直线,有可能穿过窗口,也有可能不穿过窗口,如图87所示。图中所示的两条直线都不符合情况②的要求,但一条直线(P 1P 2)穿过窗口,另一直线(P 3P 4)不守过窗口。对这类直线可以进行如下处理:取窗口外的一个端点与窗口边界比较以确定可排除直线的哪一部分,然后,把直线剩下的部分与其他边界比较,这样一直到直线全部被排除或确定直线的哪一部分在窗口之为止。可按“左、右、下、上”的次序建立检查直线端点与窗口边界关系的算法。 下面介绍对图3所示的两条直线进行处理的过程。从直线P 1P 2的下端点P 1开始,依次检查窗口的左、上、右及下边界,可发现此点在窗口之下(因为区域码的第三位为1)。然后求得直线与下边界的交点P 1排除线段P 1 P 1这样直线就缩短为P 1 P 2。因为P 2在边界之外,将此端点与各边界比较,可发现此端点在窗口上面。计算出交点P 2线段P 1P 2保留下来。这样就完成了对这条线的处理并开始处理下一直线P 3 P 4。端点P 3在窗口之左,可计算出交点3P '讲裁剪掉线段33P P '。检查43P P '的区域码,可发现直线的这一剩余部分在窗口之下故也可排除。 由于窗口边界均平行于坐标轴,所以直线与窗口边界的交点可以用直线方程的各参数很方便地求出,对于一条端点坐标为()11,y x 及()22,y x 的直线,其与一垂直边界的交点的y 坐标可以用下式计算: ()11x x m y y -+= 这里互的取值可取x 或m ax w x ,斜率m 可用公式())1212x x y y m --=计算。同样地,与水平边界交点的x 坐标可用下式计算: ()m y y x x 11-+= 这里y 的值可取几m in w y 或m ax w y 。 下面是区域码直线裁剪算法的C 语言描述,其中每个端点的区域码用4元素布尔数组表示。 区域码直线裁剪算法代码 Void Line_Clipping(x1,y1,x2,y2,xw_xmin,yw_ymin,xw_max,yw_max) float x1,y1,x2,y2,xw_xmin,yw_ymin,xw_max,xw_max,yw_max; /*()11,y x 和()22,y x 是线段端点坐标*/ { Int draw,done; Int code1,code2,code; float x,y; Draw=FALSE; done=FALSE; code1=get_code(x1,y1,xw_xmin,yw_ymin,xw_max,yw_max); code2=get_code(x2,y2,xw_xmin,yw_ymin,xw_max,yw_max); while(! Done) { if (code1= =0 && code= =2) { draw=TRUE;done=TRUE; } else if (code1 & code2 !=0) done=TRUE; else { If (code1 !=0) code=code1; else code=code2; if (code&TOP!=0) { y=yw_ymax; x=x1+(y-y1)*(x2-x1)/(y2-y1); } else if (code&BOTTOM!=0) { y=yw_ymin; x=x1+(y-y1)*(x2-x1)/(y2-y1); } else if (code&RTGHT!=0) { x=xw_xmax; y=y1+(x-x1)*(y2-y1)/(x2-x1); } else if (code&LIFT!=0) { x=xw_xmin; y=y1+(x-x1)*(y2-y1)/(x2-x1); } if (code= =code1) { x1=x; y1=y; code1=get_code{x1,y1,xw_xmin,yw_ymin,xw_max,yw_max}; } else { x2=x;y2=y; code2=get_code{x2,y2,xw_xmin,yw_ymin,xw_max,yw_max}; } If (draw) Draw_Line(x1,y1,x2,y2); } int get_code(x,y,xw_xmin,yw_ymin,xw_max,yw_max) float x,y,xw_xmin,yw_ymin,xw_max,yw_max; { int code; code=0; if (y>yw_ymax) code| =TOP; else if (y if (x>xw_xmax) code|=RIGHT; else if (x 4 Liang -Barsky 算法 Liang (梁友栋)-Barsky 算法又称为参数方程法。首先写出端点()11,y x 及()22,y x 之间连线的参数方程如下: ()xu x u x x x x ?+=-+=1121 ()yu y u y y y y ?+=-+=1121 其中,1212,y y y x x x -=?-=?。参数u 可取0 1之间的值,坐标()y x ,表示此围的u 值定义的直线上的一个点。当u =0时,()()11,,y x y x =,直线的另一端u =0时, ()()22,,y x y x =。 我们发现,如果直线上的某点处于()y x ,及()min min ,w w y x 及()max max ,w w y x 所定义的窗口之,则满足以下条: min w x ≤xu x ?+1≤m ax w x m in w y ≤yu y ?+1≤m ax w y 这四个不等式可以表示为: k up ≤k q , k=1,2,3,4 其中,参数q p ,定义为; ,1x p ?-= min 11w x x q -= ,2x p ?= 1max 2x x q w -= ,3y p ?-= min 13w y y q -= ,4y p ?= 1max 4y y q w -= 按照直线与窗口边界的相对位置,可分为以下几种倩况。 ①任何一条与窗口边界平行的直线,其0=k p ,此处k 值表示取哪一个边界(k =1,2,3及4,分别相应于左、右、下及上边界)。如果对某一k 值,还满足0 ②如果0≠k q ,则情况较复杂。此时,可把直线按从()11,y x 到()22,y x 连线方向作为正向,将此直线无限延伸,同时把各窗口边界也无限延伸(见图3);然后分以下两种情况讨论: a 当0 b 当0>k q 时,情况相反。即直线的延伸线是由窗口边界延伸线的部到外部。 以图3中的直线1L 为例,说明以上两种情况。表1.1给出1L 的延伸线相对于4个边界延伸线的方向及k q 取值。 表1.1 直线方向与k 边界 k P 取值 方向 交点 左 ()0121<--=?-=x x x p 由外到 1u 右 ()0122>-=?=x x x p 由到外 下 ()0123<-=?-=y y y p 由外到 上 ()0124>-=?=y y y p 由到外 2u 当0≠k q 时,可以用下式计算出一参数u ,此u 值应于直线延伸线与窗口边界k 的延 伸线的交点: k k p q u = 对于每条直线,可计算出一对u 值(1u 及2u ),由此两参数可判断直线是如何裁剪的。 图3 直线与窗口边界的相对 其中,1u 的值可由从窗口边界外发出进人窗口边界之的直线(0 k k k p q r =,取各k r 中最大值即为回1u ,2u 可由自窗口边界发出至窗口边界外的直线(0>k P )所涉及的窗口边界确定,同样可计算出这些窗口边界的k r 值,取各k r 中的最小值即为2u 。如果21u u >,则此直线全部在窗口外而可排除:否则,此直线在窗口,可由1u 及2u 计算出彼裁剪直线的端点。图3中标出直线1L 及2L 相应于1u 及2u 的与边界的交点。1L 的12u u >,则1L 彼裁剪,并可由1u 及2u 计算出裁剪点;而2L 的21u u >,则此直线全部彼排除。 此算法可用以下过程表示。此过程中开始把交点参数置为01=u 及12=u 。对每一窗口边界计算出相应的p 及q 值,然后用函数CliPtest 由参数p 及q 决定此直线能否彼排除或者决定交点参数是否要调整。当0>p 时,用参数r 修改1u 值:当0>p 时,用参数r 修改2u 值。如果最后的结果为21u u >,则排除此直线;否则,只有当新的值使直线缩短时,才修改相应的参数u 。当时且00<=q p ,可排除此直线,因为直线与边界平行且在边界之外。如果四个p 及q 均被检查而直线被排除,则被裁剪直线的端点可由1u 及2u 的值确定。 Liang -Barsky 裁剪算法代码 Void Line_Clipping (x1,y1,x2,y2,rect) Float x1,y1,x2,y2,*rect; { Float dx,dy,t0,t1; t0=0;t1=1; if (Clip Top(-dx,x1-rect->xw_xmin,&t0,&t1)) if (Clip Top(dx,rect->xw_xmax-x1,&t0,&t1)) { dy=y2-y1; if (Clip Top(-dy,y1-rect->yx_xmin,&t0,&t1)) if (Clip Top(dy,rect->xw_xmax-y1,&t0,&t1)) { Line ((int) (x1+t0*dx),(int) (y1+t0*dy)) (int) (x1+t1*dx) (int) (y1+t1*dy)); return; } } writeText(“p1p2完全不可见”); } V oid Line_Clipping(q,d,*t0,*t1) float q,d,*t0,*t1; { float r; if (r>t1) return(FALSE); else if (r>t0) { t0=r; return (TRUE); } } else if (q>0) { r=d/q; if (r return (FALSE); else if (r { t1=r; return (TRUE); } } else if (d<0) return (FALSE); return (TRUE); } 5 两种算法比较 Cohen-Sutherland直线裁剪算法与Liang-Barsky裁剪算法所需的各种运算的次数进行比较,列表1.2如下: 由上表可知:在求被裁剪计算直线与窗口边界的交点交点时,Liang-Barsky算法的效率也高于Cohen-Sutherland算法。 6结论 区域码直线裁剪算法方法直观方便,速度较快,是一种较好的裁剪方法,但有两个问题还有待进一步解决。 (1)由于采用位逻辑乘的运算,这在有些高级语言中是不便进行的。 (2)全部舍弃的判断只适合于那些仅在窗口的线段,对于跨越3个区域的线段(如d线 段),就不能一次作出判别面舍弃它们。