Cohen-Sutherland线裁剪算法

本文详细介绍了Cohen-Sutherland直线裁剪算法,一种用于计算机图形学中的高效裁剪方法。该算法通过编码简化直线裁剪过程,判断直线段是否完全可见、完全不可见或部分可见,并通过计算交点进行裁剪。文章提供了算法的基本流程、细节及核心代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

直线裁剪算法


裁剪:计算机内部存储的图形往往比较大,而屏幕显示的只是图形的一部分,因此需要确定图形哪些部分落在显示区之内,哪些落在显示区之外,这个选择的过程就称为裁剪。

1. 最简单的裁剪方法是把直线扫描变换为各点是否在窗口内。但此方法太费时,效率比较低,一般不可取。

对于任意一点P(x,y)若满足下列不等式,则点P在矩形窗口内

 

2. 直线段的裁剪——>复杂图形裁剪的基础

要裁剪一条直线段,首先要判断此直线的位置:
1)它是否完全落在裁剪窗口内?
2)它是否完全在窗口外?
3)如果不满足以上两个条件,则计算它与一个或多个裁剪边界的交点。


Cohen-Sutherland算法(编码裁剪算法):


首先对直线段的端点进行编码。

基本思想:对每条直线段分三种情况处理:


1)若点p1和p2完全在裁剪窗口内——>“简取”之(保留这条直线)



2)若点p1(x1,y1),p2(x2,y2)均在窗口外,且满足下列四个条件之一:——>“简弃”之(不要了)



3)如果直线段既不满足“简取”的条件,也不满足“简弃”的条件?


——>需要对直线段按交点进行分段,分段后判断直线是“简取”还是“简弃”。


 

 

Cohen-Sutherland编码方法:

 


 


裁剪一条线段时,先求出端点p1和p2的编码code1和code2,然后进行二进制“或”运算和“与”运算。





【若这两个条件均不成立,则需要求出直线段与窗口边界的交点在交点处把线段一分为二。】

如裁剪如下图所示的直线段P1P2:

首先对P1P2进行编码

令直线段与窗口左边界的交点为P3,
则可知P1P3必在窗口外,可以简弃之。

再令直线段与窗口下边界的交点为P4,


剩下的直线段(P3P4)再进行进一步判断,code1|code2=0,全在窗口中,简取之。【点P在窗口边界也属于在窗口内】

 

 

算法的基本流程:

  1. 检查线段 P1P2 是否为完全可见,或完全不可见,对于这两种情况或完全取之,或完全弃之,否则转 2 .
  2. 找到 P1P2 在窗口外的一个端点 P1 (或 P2)
  3. 用窗口的边与 P1P2 的交点取代端点 P1 (或 P2)
  4. P1P2 线段是否完全可见,若是,则结束,否则转 2 继续执行

 

 

算法的细节:

 

直线与窗口边界的交点可如下求解:

已知直线和其两端点 (X_{1},Y_{1}) , (X_{2},Y_{2}) , 与水平线 Y=K 的交点为 (X,Y)

X=X_{1}+(X_{2}-X_{1})*(K-Y_{1})/(Y_{2}-Y_{1})     

Y=K

与铅垂线 X=R 的交点为

X=R

Y=Y_{1}+(Y_{2}-Y_{1})*(R-X_{1})/(X_{2}-X_{1})     

 

在进行裁剪时除了要求与边界的交点外,还要判断端点与窗口位置的关系(为了清楚应该求直线与那些边界的交点):

设一端点 P 的编码为 XXXX  

若 XXXX & 0001  != 0 ,端点与左边界相交点;

若 XXXX & 0010  != 0 ,端点与右边界相交点;

若 XXXX & 0100  != 0 ,端点与下边界相交点;

若 XXXX & 1000  != 0 ,端点与上边界相交点;

 

 


存在的问题:




如图中线段全部在窗口外部,但是对两段端点进行或/与运算时,需要再次取交点进行运算,最坏情况下,被裁剪线段与窗口4条边计算交点,然后所得的裁剪结果却可能是全部舍弃。

 

 

核心代码:

 

    def getcode(self, pos, rect):
        code = 0
        x = pos.x()
        y = pos.y()

        if x < rect.left():
            code = code | 0x01
        else:
            code = code & 0xfe

        if x > rect.right():
            code = code | 0x02
        else:
            code = code & 0xfd

        if y < rect.top():
            code = code | 0x04
        else:
            code = code & 0xfb

        if y > rect.bottom():
            code = code | 0x08
        else:
            code = code & 0xf7

        return code

    def line_clip(self, line1, rt):
        done = False  # 裁剪完成标志
        p1 = line1.p1()
        p2 = line1.p2()
        c1 = self.getcode(p1, rt)
        c2 = self.getcode(p2, rt)
        p = QPoint(0, 0)
        line2 = QLine(p, p)

        while not done:
            if c1 == 0 and c2 == 0:  # 线段完全可见
                line2 = QLine(p1, p2)
                done = True
            elif c1 & c2 != 0:  # 线段完全不可见
                line2 = QLine(QPoint(0, 0), QPoint(0, 0))
                done = True
            else:  # 部分可见
                if c1 != 0:  # p0不可见
                    code = c1
                else:  # p0可见,则p1一定不可见
                    code = c2
                if code & 0x01 != 0:  # 线段与窗口的左边有交
                    x = rt.left()
                    m = (p2.y() - p1.y()) / (p2.x() - p1.x())
                    y = p1.y() + int((x - p1.x()) * m)
                    p = QPoint(x, y)
                elif code & 0x08 != 0:  # 线段与窗口的下边有交
                    y = rt.bottom()
                    m = (p2.x() - p1.x()) / (p2.y() - p1.y())
                    x = p1.x() + int((y - p1.y()) * m)
                    p = QPoint(x, y)
                elif code & 0x02 != 0:  # 线段与窗口的右边有交
                    x = rt.right()
                    m = (p2.y() - p1.y()) / (p2.x() - p1.x())
                    y = p1.y() + int((x - p1.x()) * m)
                    p = QPoint(x, y)
                elif code & 0x04 != 0:  # 线段与窗口的上边有交
                    y = rt.top()
                    m = (p2.x() - p1.x()) / (p2.y() - p1.y())
                    x = p1.x() + int((y - p1.y()) * m)
                    p = QPoint(x, y)
                if code == c1:
                    p1 = p
                    c1 = self.getcode(p1, rt)
                else:
                    p2 = p
                    c2 = self.getcode(p2, rt)

        return line2

 

加上UI界面实现效果:

 

 

PS: 如需参考完整代码,请移步:https://download.csdn.net/download/qq_42185999/11892408  进行下载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值