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

个人博客:www.vectormoon.net

算法简介

    编码算法是最早、最流行的线段裁剪算法,该算法采用区域检验的方法,能够快速有效地判断一条线段与裁剪窗口的位置关系,对完全接受或完全舍弃的线段无需求交,即可直接识别。

算法思想

    编码算法将整个画布分成9个区域,如下图所示:

在这里插入图片描述

    根据线段端点所在位置,给每个端点一个四位二进制码(称为区域码)。四位区域码的4位从左到右依次表示上、下、右、左。区域码的任何为赋值为1代表端点落在相应的区域中,否则为0。

区域码的生成

    区域码的生成有两种方法:

     1.比较法

     根据上面提到的区域编码规则可知,在确定区域码每位的值时,可通过比较端点坐标值 ( x , y ) (x,y) (x,y)和裁剪边界来确定区域码各位的值:

  • 如果 x < x m i n x<x_{min} x<xmin,表示该点在裁剪窗口左边界的左边,则第1位置1,否则置0;
  • 如果 x > x m a x x>x_{max} x>xmax,表示该点在裁剪窗口右边界的右边,则第2位置1,否则置0;
  • 如果 y < y m i n y<y_{min} y<ymin,表示该点在裁剪窗口下边界的下边,则第3位置1,否则置0;
  • 如果 y < y m a x y<y_{max} y<ymax,表示该点在裁剪窗口上边界的上边,则第4位置1,否则置0;

     2.差值法

     按照下列两步可以确定区域码各位的值:

  • 计算端点坐标和裁剪边界之间的差值
  • 用各差值符号来设置区域码各位的值:第1位为 x − x m i n x-x_{min} xxmin的符号位;第2位为 x − x m a x x-x{max} xxmax;第3位为 y − y m i n y-y_{min} yymin的符号位;第4位为 y − y m a x y-y{max} yymax的符号位。

对线段的裁剪处理

    根据线段和裁剪窗口的关系可分三种情况处理:

在这里插入图片描述

  • 线段完全在裁剪窗口之内
        两个端点的区域码都为0000,则该线段完全在裁剪窗口内。如上图: P 5 P 6 P_5P_6 P5P6

  • 线段完全在裁剪窗口之外
        两个端点的区域码相与的结果不为0000,则该线段完全在裁剪窗口之外。如上图: P 9 P 10 P_9P_{10} P9P10

  • 其他
        上图中 P 1 P 2 , P 3 P 4 , P 7 P 8 P_1P_2,P_3P_4,P_7P_8 P1P2,P3P4,P7P8都是此类问题; P 1 P 2 , P 7 P 8 P_1P_2,P_7P_8 P1P2,P7P8显见属于一半落在窗口内一半落在窗口外,需要进行求交运算;而 P 3 P 4 P_3P_4 P3P4虽然完全落在窗口外但是条件不被第二种情况所适用也需要进行求交运算

     求交过程:首先对线段外端点(落在窗口外的点)与一条裁剪边界比较来确定需要裁剪多少线段;然后,将线段的剩下部分与其他裁剪边界对比,直到该直线完全落在窗口内或者被舍弃。
    实际算法实现只有在检测到区域码的某位为1时,才把线段和对应裁剪窗口进行求交运算。整个算法的流程如下图所示:

在这里插入图片描述

代码实现

def Cohen-Sutherland(p_list, x_min, y_min, x_max, y_max):
    """线段裁剪
    :param p_list: (list of list of int: [[x0, y0], [x1, y1]]) 线段的起点和终点坐标
    :param x_min: 裁剪窗口左上角x坐标
    :param y_min: 裁剪窗口左上角y坐标
    :param x_max: 裁剪窗口右下角x坐标
    :param y_max: 裁剪窗口右下角y坐标
    :return: (list of list of int: [[x_0, y_0], [x_1, y_1]]) 裁剪后线段的起点和终点坐标
    """
    result = []
    if y_min > y_max:
        y_min, y_max = y_max, y_min
    x0, y0 = p_list[0]
    x1, y1 = p_list[1]

    while 1:
        code0 = 0 #1_left, 2_right, 4_down, 8_up
        code1 = 0 #1_left, 2_right, 4_down, 8_up
        #calc code0
        if x0 < x_min:
            code0 += 1
        elif x0 > x_max:
            code0 += 2
        if y0 < y_min:
            code0 += 4
        elif y0 > y_max:
            code0 += 8
        #calc code1
        if x1 < x_min:
            code1 += 1
        elif x1 > x_max:
            code1 += 2
        if y1 < y_min:
            code1 += 4
        elif y1 > y_max:
            code1 += 8
        #inside
        if (code0 | code1) == 0:
            result = [[x0, y0], [x1, y1]]
            break
        #outside
        elif (code0 & code1) != 0:
            result.append([0,0])
            result.append([0,0])
            break
        #otherwise
        else:
            if code0 == 0:
                x0, x1 = x1, x0
                y0, y1 = y1, y0
                code0, code1 = code1, code0
            #1_left, 2_right, 4_down, 8_up
            if (code0 & 1):
                y0 = int(y0 + ((x_min-x0) * (y0-y1)/(x0-x1)) + 0.5)
                x0 = x_min
            if (code0 & 2):
                y0 = int(y0 + ((x_max-x0) * (y0-y1)/(x0-x1)) + 0.5)
                x0 = x_max
            if (code0 & 4):
                x0 = int(x0 + ((y_min-y0) * (x0-x1)/(y0-y1)) + 0.5)
                y0 = y_min
            if (code0 & 8):
                x0 = int(x0 + ((y_max-y0) * (x0-x1)/(y0-y1)) + 0.5)
                y0 = y_max
	return result
  • 16
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值