• 基于Python实现的图形绘制系统


    目录
    综述 2
    算法介绍 2
    直线生成 3
    DDA算法 3
    Bresenham算法 3
    多边形的生成 6
    椭圆的生成 中点圆生成算法 6
    在实现的时候遇到的小问题 8
    曲线的生成 9
    B样条曲线 10
    图元的平移 11
    图元的旋转 11
    图元的缩放 11
    线段的裁剪 12
    Cohen-Sutherland算法 12
    Liang-Barsky 算法 13
    6. 具体的操作步骤如下: 13
    额外的一些算法 14
    2. 边表(Edge Table):所有边按下端点的y坐标归类 14
    3. 排序。如果有新边插入 AET,则对 AET 中各边排序; 16
    镜像变换 18
    系统介绍 18
    CLI 18
    GUI 18
    概述 18
    主要布局 19
    画布部分 19
    原理概述 20
    色彩笔宽部分 20
    图元操作部分 20
    线段裁剪部分 20
    重置画布部分 20
    主题部分 20
    总结 21
    参考文献 21
    B样条曲线 22
    图元的旋转 22
    Cohen-Sutherland算法 22
    扫描线算法 22
    图标 22

    综述
    本实验主要为了实现一个图形绘制系统,主要内容包括了底层算法实现,命令行系统完善和图形界面系 统(pyqt)的实现;
    根据大实验的具体要求,主要通过Python3完成了以下模块的内容: 核心算法模块:cg algorithm.py(除Liang-Barsky完成)
    命令行界面程序:cg cli.py(完成)
    用户交互界面(待做)
    原理概述
    因此画布的主要使命就是:根据鼠标的位置,点击,移动,按住鼠标等事件,新建图元加入到scene()队列里面 和item_dict字典或者更新正在工作的图元信息,然后框架就会将它绘制出来;对于选择图元进行操作的动 作则是通过selected_id获得当前选中的图元信息,然后在item_dict中进行检索相应的元素然后根据鼠标 事件信息对p_list进行修改和调用algorithm里面的相应函数;
    色彩笔宽部分
    我在main_window部分增加了笔宽和颜色对话框的接口,可以获得相应的颜色和笔宽信息同时更新到画 布类中,在画布中存储这些信息,然后在建立item的时候传进去修改QPainter的相应参数即可;
    图元操作部分
    图元操作部分为了能够便操作边显示变化,实际上就需要在mouseMoveEvent事件中进行状态的更新,而不是等结束之后再更新。我在translate,rotate,scale做的相似的操作都是使用一些中间变量记录和更新, 具体而言,在平移操作translate中,我是用self.centerx,self.centery记录当前的中心,然后传递给
    temp_item调用translate函数更新p_list,然后在调用getcenterpoint函数根据p_list更新下一时刻的
    centerx,centery;在rotate里面每一次旋转实际上是要旋转当前点-中心点-上一步的点三者组成的夹角,因此我用firstx,firsty记录上一步点的信息;scale与rotate相似,需要计算的是上一步点-中心点的长度比上当前点-中心点的长度,用一个中间变量firstx,firsty同样可以解决;
    线段裁剪部分
    线段裁剪部分为了实现用来标识裁剪框的临时的矩形,我在mousePressEvent的时候建立了一个临时的
    item,并且加入到scene()队列中,这个item是Rect类型,实际上算法调用的是多边形的算法进行计算;在
    mouseMoveEvent的时候通过鼠标事件进行坐标p_list更新,然后在mouveReleaseEvent的时候从
    scene()中删除这个图元信息即可;
    重置画布部分
    主要是将一些变量重置,初始化;这里提到的主要原因是因为一个bug,QT中listwidget变量要清除所有元素 的时候需要先将绑定的槽函数disconnect然后再执行clear()操作,否则会出现段错误,参考自博客;这个错误之前的表现是,一旦有图元移动旋转缩放过,然后重置画布,就会卡死;
    上述是5月份的版本,现在的重置画布我不再在canvas里面进行操作变量重置初始化工作,而是在
    cg_MainWindow.py中新建了一个scene然后用setScene直接将这个新的scene绑定到canvas上面;
    主题部分
    我查阅了QT的相关文档以及多篇博客学习了QSS的使用方法,并且做了两个主题可以在菜单栏进行切换;

    #!/usr/bin/env python
    # -*- coding:utf-8 -*-
    
    # 本文件只允许依赖math库
    import math
    
    
    def draw_line(p_list, algorithm):
        """绘制线段
    
        :param p_list: (list of list of int: [[x0, y0], [x1, y1]]) 线段的起点和终点坐标
        :param algorithm: (string) 绘制使用的算法,包括'DDA''Bresenham',此处的'Naive'仅作为示例,测试时不会出现
        :return: (list of list of int: [[x_0, y_0], [x_1, y_1], [x_2, y_2], ...]) 绘制结果的像素点坐标列表
        """
        temp_list_int = []
        for [x, y] in p_list:
            temp_list_int.append([round(x), round(y)])
        p_list=temp_list_int
        x0, y0 = p_list[0]
        x1, y1 = p_list[1]
        result = []
        if x0==0 and y0==0 and x1 ==0 and y1==0:
            return result
        if algorithm == 'Naive':
            if x0 == x1:
                for y in range(y0, y1 + 1):
                    result.append((x0, y))
            else:
                if x0 > x1:
                    x0, y0, x1, y1 = x1, y1, x0, y0
                k = (y1 - y0) / (x1 - x0)
                for x in range(x0, x1 + 1):
                    result.append((x, int(y0 + k * (x - x0))))
        elif algorithm == 'DDA':
            dx = x1 - x0
            dy = y1 - y0
            if (abs(dx) > abs(dy)):
                k = abs(dx)
            else:
                k = abs(dy)
            if k==0:
               result.append([x1,y1])
               return result
            delta_x = dx / k
            delta_y = dy / k
            for i in range(k + 1):
                tempx = int(x0 + i * delta_x)
                tempy = int(y0 + i * delta_y)
                result.append([tempx, tempy])
            #
            #
            # if x0 > x1:
            #     x1, x0 = x0, x1
            #     y1, y0 = y0, y1
            # dx = x1 - x0
            # dy = y1 - y0
            # k = 999999999
            # k_inf = 0
            # if dx == 0:
            #     k_inf = 1
            # else:
            #     k = dy / dx
            # x = round(x0)
            # y = round(y0)
            # '''起始点'''
            # if k > -1 and k < 1:
            #     while True:
            #         if x > x1:
            #             print("herer3")
            #             break
            #         result.append((round(x), round(y)))
            #         x = x + 1
            #         y = y + k
            # elif k >= 1:
            #     '''Y最大位移'''
            #     while True:
            #         if y > y1:
            #             print("herer1",y,y1)
            #             break
            #         result.append((round(x), round(y)))
            #         y = y + 1
            #         if k_inf == 0:
            #             x = x + 1 / k
            #         else:
            #             x = x
            # else:
            #     while True:
            #         if y < y1:
            #             print("herer2")
            #             break
            #         result.append((round(x), round(y)))
            #         y = y - 1
            #         if k_inf == 0:
            #             x = x - 1 / k
            #         else:
            #             x = x
    
            '''pass'''
            '''参考:https://www.youtube.com/watch?v=W5P8GlaEOSI
            https://blog.csdn.net/u010429424/article/details/77834046
            '''
        elif algorithm == 'Bresenham':
            if x0 > x1:
                x1, x0 = x0, x1
                y1, y0 = y0, y1
            dx = x1 - x0
            dy = y1 - y0
            f = 0
    
            if 0 <= dy <= dx:
                x = x0
                y = y0
                while x <= x1:
                    result.append((round(x), round(y)))
                    if f + dy + f + dy - dx < 0:
                        f = f + dy
                    else:
                        f = f + dy - dx
                        y = 1 + y
                    x = x + 1
            elif dy >= 0 and dy > dx:
                x = x0
                y = y0
                while y <= y1:
                    result.append((round(x), round(y)))
                    if f - dx + f - dx + dy > 0:
                        f = f - dx
                    else:
                        f = f - dx + dy
                        x = x + 1
                    y = y + 1
            elif dy <= 0 and -dy <= dx:
                x = x0
                y = y0
                while x <= x1:
                    result.append((round(x), round(y)))
                    if f + dy + f + dy + dx > 0:
                        f = f + dy
                    else:
                        f = f + dy + dx
                        y = y - 1
                    x = x + 1
            elif dy <= 0 and -dy > dx:
                x = x0
                y = y0
                while y >= y1:
                    result.append((round(x), round(y)))
                    if f + dx + f + dx + dy < 0:
                        f = f + dx
                    else:
                        f = f + dx + dy
                        x = x + 1
                    y = y - 1
    
            ''' pass'''
            '''参考:https://www.youtube.com/watch?v=RGB-wlatStc&t=415s
            https://segmentfault.com/a/1190000002700500'''
        return result
    
    
    def draw_polygon(p_list, algorithm):
        """绘制多边形
    
        :param p_list: (list of list of int: [[x0, y0], [x1, y1], [x2, y2], ...]) 多边形的顶点坐标列表
        :param algorithm: (string) 绘制使用的算法,包括'DDA''Bresenham'
        :return: (list of list of int: [[x_0, y_0], [x_1, y_1], [x_2, y_2], ...]) 绘制结果的像素点坐标列表
        """
        result = []
        for i in range(len(p_list)):
            line = draw_line([p_list[i - 1], p_list[i]], algorithm)
            result += line
        return result
    
    
    def draw_ellipse(p_list):
        """绘制椭圆(采用中点圆生成算法)
    
        :param p_list: (list of list of int: [[x0, y0], [x1, y1]]) 椭圆的矩形包围框左上角和右下角顶点坐标
        :return: (list of list of int: [[x_0, y_0], [x_1, y_1], [x_2, y_2], ...]) 绘制结果的像素点坐标列表
        """
    
        result = []
        # tempx0,tempy0=p_list[0]
        # tempx1,tempy1=p_list[1]
        # print("temp0",tempx0,tempy0)
        # print("temp1",tempx1, tempy1)
        # x0=min(tempx0,tempx1)
        # y0=max(tempy0,tempy1)
        # x1=max(tempx0,tempx1)
        # y1=min(tempy0,tempy1)
        x0, y0 = p_list[0]
        x1, y1 = p_list[1]
        if x0==x1 and y0==y1:
            result.append((x0,y0))
            return result
        if y0==y1:
            result=draw_line([[x0,y0],[x1,y1]],"DDA")
            return result
    
    
        rx = abs(x1 - x0) / 2
        ry = abs(y1 - y0) / 2
        xc = x0 + rx
        yc = y0 - ry
        p = ry * ry - rx * rx * ry + (rx * rx / 4)
    
        tx = 0
        ty = ry
        step=1
        packet = []
        #packet.append((round(tx), round(ty)))
        packet.append((tx, ty))
        # print("begin",rx,ry,xc,yc)
        while 2 * ry * ry * tx < 2 * rx * rx * ty:
            #print(tx,ty)
            #  print("left",2 * ry * ry * tx," right",2 * rx * ty)
    
            if p < 0:
                tx = tx + step
                p = p + 2 * ry * ry * tx + ry * ry
            elif p >= 0:
                tx = tx + step
                ty = ty - step
                p = p + 2 * ry * ry * tx - 2 * rx * rx * ty + ry * ry
            # print("herer")
            packet.append((tx, ty))
            # packet.append((round(tx), round(ty)))
        # print("out left", 2 * ry * ry * tx, " right", 2 * rx * ty)
        p2 = ry * ry * (tx + 0.5) * (tx + 0.5) + rx * rx * (ty - 1) * (ty - 1) - rx * rx * ry * ry
        # print("next")
        while True:
            #   print(tx, ty)
            if ty <= 0:
                '''循环到(rx,0)'''
                break
            if p2 > 0:
                ty = ty - step
                p2 = p2 - 2 * rx * rx * ty + rx * rx
                #print(p2)
            else:
                tx = tx + step
                ty = ty - step
                p2 = p2 + 2 * ry * ry * tx - 2 * rx * rx * ty + rx * rx
    
            # packet.append((round(tx), round(ty)))
            packet.append((tx, ty))
    
        for k in range(len(packet)):
            tempx, tempy = packet[k]
            result.append((round(xc + tempx), round(yc + tempy)))
            result.append((round(xc + tempx), round(yc - tempy)))
            result.append((round(xc - tempx), round(yc + tempy)))
            result.append((round(xc - tempx), round(yc - tempy)))
            # result.append((xc + tempx, yc + tempy))
            # result.append((xc + tempx, yc - tempy))
            # result.append((xc - tempx, yc + tempy))
            # result.append((xc - tempx, yc - tempy))
    
    
        return result
    
    
    ##B 样条曲线的辅助函数
    def getLambda(i, r, t, u):
        if abs(u[i + 4 - r] - u[i]) <= 1e-7:
            return 0
        else:
            return (t - u[i]) / (u[i + 4 - r] - u[i])
    
    
    def deBoorCox_X(i, r, t, p_list, u):
        tempx, tempy = p_list[i]
        # if t==2:
        #    print("r:",r)
        if r == 0:
            return tempx
        else:
            return getLambda(i, r, t, u) * deBoorCox_X(i, r - 1, t, p_list, u) + (1 - getLambda(i, r, t, u)) * deBoorCox_X(
                i - 1, r - 1, t, p_list, u)
    
    
    def deBoorCox_Y(i, r, t, p_list, u):
        tempx, tempy = p_list[i]
        if r == 0:
            return tempy
        else:
            return getLambda(i, r, t, u) * deBoorCox_Y(i, r - 1, t, p_list, u) + (1 - getLambda(i, r, t, u)) * deBoorCox_Y(
                i - 1, r - 1, t, p_list, u)
    
    
    def draw_curve(p_list, algorithm):
        """绘制曲线
    
        :param p_list: (list of list of int: [[x0, y0], [x1, y1], [x2, y2], ...]) 曲线的控制点坐标列表
        :param algorithm: (string) 绘制使用的算法,包括'Bezier''B-spline'(三次均匀B样条曲线,曲线不必经过首末控制点)
        :return: (list of list of int: [[x_0, y_0], [x_1, y_1], [x_2, y_2], ...]) 绘制结果的像素点坐标列表
        """
        result = []
        q_list = []
        # print("here")
        if algorithm == 'Bezier':
            # 使用了De Casteljau’s Algorithm
            n = len(p_list)  # 次数
            # print(n)
            qy = []
            qx = []
            for i in range(len(p_list)):
                tempx, tempy = p_list[i]
                # print(tempx, tempy)
                qx.append(tempx)
                qy.append(tempy)
                # q_list.append([tempx, tempy])
    
            xarray = [0 for _ in range(n)]
            yarray = [0 for _ in range(n)]
    
            x = qx[0]
            y = qy[0]
            # print("n:",n)
            for tt in range(0, 1000 * n + 1, 1):
                t = (tt / 1000) / n
                # print(t)
                for i in range(1, n, 1):  # 迭代的次数
                    for j in range(0, n - i, 1):
    
                        if i == 1:
                            xarray[j] = qx[j] * (1 - t) + qx[j + 1] * t
                            yarray[j] = qy[j] * (1 - t) + qy[j + 1] * t
                            continue
                        else:
                            xarray[j] = xarray[j] * (1 - t) + xarray[j + 1] * t
                            yarray[j] = yarray[j] * (1 - t) + yarray[j + 1] * t
    
                q_list.append([x, y])
                x = xarray[0]
                y = yarray[0]
    
            for i in range(0, len(q_list) - 1):
                tempx0,tempy0=q_list[i]
                tempx1,tempy1=q_list[i+1]
                line=draw_line([[round(tempx0),round(tempy0)],[round(tempx1),round(tempy1)]],'DDA')
                #line = draw_line([q_list[i], q_list[i + 1]], 'DDA')
                result += line
            # pass
        elif algorithm == 'B-spline':
            k = 4
            n = len(p_list) - 1
            q_list = []
            u = [0 for _ in range(n + k + 2)]
            for i in range(0, n + k + 1 + 1, 1):
                # print(n,i)
                u[i] = i
    
            step = 0.01
            for j in range(k - 1, n + 1, 1):
                t = u[j]
                # print("j:t",j,t)
                while t <= u[j + 1]:
                    # print(t)
                    x = deBoorCox_X(j, k - 1, t, p_list, u)
                    y = deBoorCox_Y(j, k - 1, t, p_list, u)
                    q_list.append([x, y])
                    t = t + step
            for i in range(0, len(q_list) - 1):
                tempx0,tempy0=q_list[i]
                tempx1,tempy1=q_list[i+1]
                line=draw_line([[round(tempx0),round(tempy0)],[round(tempx1),round(tempy1)]],'DDA')
                # line = draw_line([q_list[i], q_list[i + 1]], 'DDA')
                result += line
    
        # 参考: Bezier: https://www.bilibili.com/video/av33675067?p=15
        # https://www.bilibili.com/video/av66548502?p=11
        # https://blog.csdn.net/Fioman/article/details/2578895
        # B样条
        # https://www.bilibili.com/video/av33675067?t=4&p=22
        # http://www.whudj.cn/?p=535
        # https://blog.csdn.net/qingcaichongchong/article/details/52797854
        # pass
        return result
    
    
    def translate(p_list, dx, dy):
        """平移变换
    
        :param p_list: (list of list of int: [[x0, y0], [x1, y1], [x2, y2], ...]) 图元参数
        :param dx: (int) 水平方向平移量
        :param dy: (int) 垂直方向平移量
        :return: (list of list of int: [[x_0, y_0], [x_1, y_1], [x_2, y_2], ...]) 变换后的图元参数
        """
        result = []
        for [i, j] in p_list:
            #  print(i,j)
            tempx = i + dx
            tempy = j + dy
            result.append([tempx, tempy])
        return result
    
        # pass
    
    
    def rotate(p_list, x, y, r):
        """旋转变换(除椭圆外)
    
        :param p_list: (list of list of int: [[x0, y0], [x1, y1], [x2, y2], ...]) 图元参数
        :param x: (int) 旋转中心x坐标
        :param y: (int) 旋转中心y坐标
        :param r: (int) 顺时针旋转角度(°)
        :return: (list of list of int: [[x_0, y_0], [x_1, y_1], [x_2, y_2], ...]) 变换后的图元参数
        """
        # print(math.pi);
        Pi = math.pi
        Theta = -r * Pi / 180
        result = []
        for [i, j] in p_list:
            x0 = i
            y0 = j
            tempx = x + (x0 - x) * math.cos(Theta) - (y0 - y) * math.sin(Theta)
            tempy = y + (x0 - x) * math.sin(Theta) + (y0 - y) * math.cos(Theta)
            result.append([tempx, tempy])
        return result
    
    
    # 参考:https://blog.csdn.net/LearnLHC/article/details/93623031
    # pass
    
    
    def scale(p_list, x, y, s):
        """缩放变换
    
        :param p_list: (list of list of int: [[x0, y0], [x1, y1], [x2, y2], ...]) 图元参数
        :param x: (int) 缩放中心x坐标
        :param y: (int) 缩放中心y坐标
        :param s: (float) 缩放倍数
        :return: (list of list of int: [[x_0, y_0], [x_1, y_1], [x_2, y_2], ...]) 变换后的图元参数
        """
        result = []
    
        for [i, j] in p_list:
            qx=(i-x)*s
            qy=(j-y)*s
            tempx=qx+x
            tempy=qy+y
            result.append([tempx, tempy])
        return result
    
        # def _scale(p:list)->list:
        #     qx,qy=(p[0]-x)*s,(p[1]-y)*s
        #     return [int(qx+x),int(qy+y)]
        # return list(map(_scale,p_list))
        # pass
    
    
    def Checklocation(x, y, x_min, y_min, x_max, y_max) -> int:
        result = 0
        INSIDE = 0  # 0000
        LEFT = 1  # 0001
        RIGHT = 2  # 0010
        BOTTOM = 4  # 0100
        TOP = 8  # 1000
    
        result = INSIDE
    
        if x < x_min:
            result = result | LEFT
        elif x > x_max:
            result = result | RIGHT
        if y < y_min:
            result = result | BOTTOM
        elif y > y_max:
            result = result | TOP
        return result
    
    
    def clip(p_list, x_min, y_min, x_max, y_max, algorithm):
        """线段裁剪
    
        :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坐标
        :param algorithm: (string) 使用的裁剪算法,包括'Cohen-Sutherland''Liang-Barsky'
        :return: (list of list of int: [[x_0, y_0], [x_1, y_1]]) 裁剪后线段的起点和终点坐标
        """
    
        # Cohen-Sutherland 算法
        # 参考:https://www.jianshu.com/p/d512116bbbf3
        # https://www.omegaxyz.com/2018/10/29/cohen-sutherland/
        tempx_min=min(x_min,x_max)
        tempy_min=min(y_min,y_max)
        tempx_max=max(x_max,x_min)
        tempy_max=max(y_max,y_min)
    
        x_min,y_min,x_max,y_max=tempx_min,tempy_min,tempx_max,tempy_max
        if algorithm == 'Cohen-Sutherland':
            INSIDE = 0  # 0000
            LEFT = 1  # 0001
            RIGHT = 2  # 0010
            BOTTOM = 4  # 0100
            TOP = 8  # 1000
            x0, y0 = p_list[0]
            x1, y1 = p_list[1]
            outcode0 = Checklocation(x0, y0, x_min, y_min, x_max, y_max)
            outcode1 = Checklocation(x1, y1, x_min, y_min, x_max, y_max)
            # print(x0, y0)
            # print(x1, y1)
            # print(x_max, y_max, x_min, y_min)
            pass
            # success = False
            while (1):
                # print(x1, y1, x0, y0)
                # print(outcode0, outcode1)
                if bool((outcode0 | outcode1)) == 0:
                    success = True  # 在区域里面
                    break
                elif bool(outcode0 & outcode1) == 1:  # 注意这里是位运算应该用Bool类型
                    success = False  # 不在区域里面
                    break
                else:
                    # 找到界外的点
                    x = 0
                    y = 0
                    if outcode0 == 0:
                        outcode = outcode1
                    else:
                        outcode = outcode0
                    # 找到和边界相交的点 点斜式:y-b=k(x-a)
                    # y=y0+k*(x-x0) x=x0+(1/k)*(y-y0)
    
                    if outcode & TOP:
                        rk = (x1 - x0) / (y1 - y0)
                        y = y_max
                        x = x0 + rk * (y - y0)
                    elif outcode & BOTTOM:
                        rk = (x1 - x0) / (y1 - y0)
                        y = y_min
                        x = x0 + rk * (y - y0)
                    elif outcode & RIGHT:
                        k = (y1 - y0) / (x1 - x0)
                        x = x_max
                        y = y0 + k * (x - x0)
                    elif outcode & LEFT:
                        k = (y1 - y0) / (x1 - x0)
                        x = x_min
                        y = y0 + k * (x - x0)
                    if outcode == outcode1:
                        x1 = x
                        y1 = y
                        outcode1 = Checklocation(x1, y1, x_min, y_min, x_max, y_max)
                    else:
                        x0 = x
                        y0 = y
                        outcode0 = Checklocation(x0, y0, x_min, y_min, x_max, y_max)
            if success == True:
                result = [[x0, y0], [x1, y1]]
            else:
                result = [[0, 0], [0, 0]]
            return result
        elif algorithm == 'Liang-Barsky':
            # 参考:https://blog.csdn.net/soulmeetliang/article/details/79185603
            result = []
            # p_list, x_min, y_min, x_max, y_max, algorithm
    
            x0, y0 = p_list[0]
            x1, y1 = p_list[1]
            dx = x1 - x0
            dy = y1 - y0
            p = [0 for _ in range(5)]
            q = [0 for _ in range(5)]
            p[1] = -dx
            p[2] = dx
            p[3] = -dy
            p[4] = dy
            q[1] = x0 - x_min
            q[2] = x_max - x0
            q[3] = y0 - y_min
            q[4] = y_max - y0
            success = 1
            # 初始状态:
            u1 = 0
            u2 = 1
            for i in range(1, 5, 1):
                if p[i] < 0:
                    u1 = max(u1, q[i] / p[i])
                elif p[i] > 0:
                    u2 = min(u2, q[i] / p[i])
                elif p[i] == 0 and q[i] < 0:
                    success = 0
                if u1 > u2:
                    success = 0
    
            if success == 0:
                result = [[0, 0], [0, 0]]
            else:
                x00 = x0 + u1 * dx
                y00 = y0 + u1 * dy
                x11 = x0 + u2 * dx
                y11 = y0 + u2 * dy
                result = [[x00, y00], [x11, y11]]
    
            return result
    
        # pass
    class Node:
        # 定义节点
    
        def __init__(self, data):
            self._data = data
            self._next = None
    
        def get_data(self):
            return self._data
    
        def get_next(self):
            return self._next
    
        def set_data(self, ddata):
            self._data = ddata
    
        def set_next(self, nnext):
            self._next = nnext
    class SingleLinkList:
        # 定义链表
    
        def __init__(self):
            #初始化链表为空
            self._head = None
            self._size = 0
    
        def get_head(self):
            #获取链表头
            return self._head
    
        def is_empty(self):
            #判断链表是否为空
            return self._head is None
    
        def append(self, data):
            #在链表尾部追加一个节点
            temp = Node(data)
            if self._head is None:
                self._head = temp
            else:
                node = self._head
                while node.get_next() is not None:
                    node = node.get_next()
                node.set_next(temp)
            self._size += 1
    
        def remove(self, data):
            # 在链表尾部删除一个节点
            node = self._head
            prev = None
            while node is not None:
                if node.get_data() == data:
                    if not prev:
                        # 父节点为None
                        self._head = node.get_next()
                    else:
                        prev.set_next(node.get_next())
                    break
                else:
                    prev = node
                    node = node.get_next()
            self._size -= 1
    
    #参考了https://blog.csdn.net/sinat_34686158/article/details/78745670
    
    def polifill(polygon):
    
        p_list=[]
        for x,y in polygon:
            p_list.append([round(x),round(y)])
        #整数化
        polygon=p_list
        l = len(polygon)
        result=[]
        Ymax=0
        Ymin=600
        #求最大最小边
        for [x, y] in enumerate(polygon):
            if y[1] < Ymin:
                Ymin=y[1]
            if y[1] > Ymax:
                Ymax=y[1]
        height=Ymax+2
    
    
        #初始化并建立NET表
        NET = []
        for i in range(height):
            NET.append(None)
    
    
        for i in range(Ymin, Ymax + 1):
            for j in range(0, l):
                if polygon[j][1]==i:
                    #左边顶点y是否大于y0
                    if(polygon[(j-1+l)%l][1])>polygon[j][1]:
                        [x1,y1]=polygon[(j-1+l)%l]
                        [x0,y0]=polygon[j]
                        delta_x=(x1-x0)/(y1-y0)
                        NET[i] = SingleLinkList()
                        NET[i].append([x0, delta_x, y1])
    
                    # 右边顶点y是否大于y0
                    if (polygon[(j+1+l)%l][1])>polygon[j][1]:
                        [x1, y1] = polygon[(j + 1 + l) % l]
                        [x0, y0] = polygon[j]
                        delta_x = (x1 - x0) / (y1 - y0)
                        if(NET[i] is not None):
                            NET[i].append([x0, delta_x, y1])
                        else:
                            NET[i] = SingleLinkList()
                            NET[i].append([x0, delta_x, y1])
    
    
        #建立活性边表
        AET = SingleLinkList()
        for y in range(Ymin , Ymax+1):
            # 更新 start_x
            if not AET.is_empty():
                node = AET.get_head()
                while True:
                    [start_x,delta_x,ymax] = node.get_data()
                    start_x += delta_x
                    node.set_data([start_x,delta_x,ymax])
                    node = node.get_next()
                    if node is None:
                        break
    
            # 填充
            if not AET.is_empty():
                node = AET.get_head()
                x_list = []
                # 获取所有交点的x坐标
                while True:
                    [start_x,_,_] = node.get_data()
                    x_list.append(start_x)
                    node = node.get_next()
                    if node is None:
                        break
    
                # 排序
                x_list.sort()
                #print(x_list)
                # 两两配对填充
                for i in range(0,len(x_list),2):
                    x1 = x_list[i]
                    x2 = x_list[i+1]
                    for pixel in range(int(x1),int(x2)+1):
                        #image[y][pixel] = color
                        result.append([pixel,y])
    
            if not AET.is_empty():
                # 删除非活性边
                node = AET.get_head()
                while True:
                    [start_x,delta_x,ymax] = node.get_data()
                    if ymax == y:
                        AET.remove([start_x,delta_x,ymax])
                    node = node.get_next()
                    if node is None:
                        break
    
            # 添加活性边
            if NET[y] is not None:
                node = NET[y].get_head()
                while True:
                    AET.append(node.get_data())
                    node = node.get_next()
                    if node is None:
                        break
    
    
        return result
    
    
    def encode(x,y,XL,XR,YB,YT):
        LEFT = 1  # 0001 左
        RIGHT = 2  # 0010 右
        BOTTOM = 4  # 0100 下
        TOP = 8  # 1000 上
        c=0
    
        if(x<XL):
            c=c|LEFT
        if(x>XR):
            c= c|RIGHT
        if(y<YB):
            c= c|BOTTOM
        if(y>YT):
            c=c|TOP
        return c
    
    
    def draw_polygon_cut(polygon,XL,XR,YB,YT):
        #参考了https://blog.csdn.net/sinat_34686158/article/details/78745216
        LEFT = 1  # 0001 左
        RIGHT = 2  # 0010 右
        BOTTOM = 4  # 0100 下
        TOP = 8  # 1000#image=image
        result=[]
        xmin,xmax=min(XL,XR),max(XL,XR)
        ymin,ymax=min(YB,YT),max(YB,YT)
        XL,XR,YB,YT=xmin,xmax,ymin,ymax
    
        l = len(polygon)
        for i in range(l):
            [x1, y1] = polygon[i]
            [x2, y2] = polygon[(i + 1 + l) % l]
    
            flag=0
            code1=encode(x1,y1,XL,XR,YB,YT)
            code2=encode(x2,y2,XL,XR,YB,YT)
            while(code1!=0 or code2!=0):        #同时为0,在内部,不用裁剪
                if(code1 & code2 !=0):
                    flag=1
                    break
                if(code1!=0):
                    code=code1
                else:
                    code=code2
                if(LEFT & code!=0):      #点在线左边
                     x=XL
                     if(x1==x2):
                         y=y1
                     else:
                         y=(int)(y1+(y2-y1)*(XL-x1)/(x2-x1))
                elif(RIGHT & code!=0):   #点在线右边
                    x = XR
                    if(x2==x1):
                        y=y1
                    else:
                        y = (int)(y1 + (y2 - y1) * (XR - x1) / (x2 - x1))
                elif (BOTTOM & code != 0):     #点在线下边
                    y = YB
                    if(y2==y1):
                        x=x1
                    else:
                        x = (int)(x1 + (x2 - x1) * (YB - y1) / (y2 - y1))
                elif (TOP & code != 0):     #点在线上边
                    y = YT
                    if(y2==y1):
                        x=x1
                    else:
                        x = (int)(x1 + (x2 - x1) * (YT - y1) / (y2 - y1))
                if(code==code1):
                    x1=x
                    y1=y
                    code1=encode(x,y,XL,XR,YB,YT)
                else:
                    x2=x
                    y2=y
                    code2=encode(x,y,XL,XR,YB,YT)
    
            if(flag==1):
                pass
            else:
                if(x1 > x2):
                    temp = x2
                    x2 = x1
                    x1 = temp
                    temp = y2
                    y2 = y1
                    y1 = temp
    
                #DDALine(image, x1, y1, x2, y2, False)
                p_list=[]
                p_list.append([x1,y1])
                p_list.append([x2,y2])
                line=draw_line(p_list,'DDA')
                result+=line
        return result
    def x_mirror(p_list,centery):
    
        #对称轴:
        ymid=centery
        #a-x=x-b
        #ynew-ymid=ymid-y
        #ynew=2ymid-y
        #
        result=[]
        for x,y in p_list:
            #tempy=y
            tempy=2*ymid-y
            tempx=x
            result.append([tempx,tempy])
        return result
    def y_mirror(p_list,centerx):
        xmid=centerx
        result=[]
        for x,y in p_list:
            tempy=y
            tempx=2*xmid-x
            result.append([tempx,tempy])
        return result
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    • 331
    • 332
    • 333
    • 334
    • 335
    • 336
    • 337
    • 338
    • 339
    • 340
    • 341
    • 342
    • 343
    • 344
    • 345
    • 346
    • 347
    • 348
    • 349
    • 350
    • 351
    • 352
    • 353
    • 354
    • 355
    • 356
    • 357
    • 358
    • 359
    • 360
    • 361
    • 362
    • 363
    • 364
    • 365
    • 366
    • 367
    • 368
    • 369
    • 370
    • 371
    • 372
    • 373
    • 374
    • 375
    • 376
    • 377
    • 378
    • 379
    • 380
    • 381
    • 382
    • 383
    • 384
    • 385
    • 386
    • 387
    • 388
    • 389
    • 390
    • 391
    • 392
    • 393
    • 394
    • 395
    • 396
    • 397
    • 398
    • 399
    • 400
    • 401
    • 402
    • 403
    • 404
    • 405
    • 406
    • 407
    • 408
    • 409
    • 410
    • 411
    • 412
    • 413
    • 414
    • 415
    • 416
    • 417
    • 418
    • 419
    • 420
    • 421
    • 422
    • 423
    • 424
    • 425
    • 426
    • 427
    • 428
    • 429
    • 430
    • 431
    • 432
    • 433
    • 434
    • 435
    • 436
    • 437
    • 438
    • 439
    • 440
    • 441
    • 442
    • 443
    • 444
    • 445
    • 446
    • 447
    • 448
    • 449
    • 450
    • 451
    • 452
    • 453
    • 454
    • 455
    • 456
    • 457
    • 458
    • 459
    • 460
    • 461
    • 462
    • 463
    • 464
    • 465
    • 466
    • 467
    • 468
    • 469
    • 470
    • 471
    • 472
    • 473
    • 474
    • 475
    • 476
    • 477
    • 478
    • 479
    • 480
    • 481
    • 482
    • 483
    • 484
    • 485
    • 486
    • 487
    • 488
    • 489
    • 490
    • 491
    • 492
    • 493
    • 494
    • 495
    • 496
    • 497
    • 498
    • 499
    • 500
    • 501
    • 502
    • 503
    • 504
    • 505
    • 506
    • 507
    • 508
    • 509
    • 510
    • 511
    • 512
    • 513
    • 514
    • 515
    • 516
    • 517
    • 518
    • 519
    • 520
    • 521
    • 522
    • 523
    • 524
    • 525
    • 526
    • 527
    • 528
    • 529
    • 530
    • 531
    • 532
    • 533
    • 534
    • 535
    • 536
    • 537
    • 538
    • 539
    • 540
    • 541
    • 542
    • 543
    • 544
    • 545
    • 546
    • 547
    • 548
    • 549
    • 550
    • 551
    • 552
    • 553
    • 554
    • 555
    • 556
    • 557
    • 558
    • 559
    • 560
    • 561
    • 562
    • 563
    • 564
    • 565
    • 566
    • 567
    • 568
    • 569
    • 570
    • 571
    • 572
    • 573
    • 574
    • 575
    • 576
    • 577
    • 578
    • 579
    • 580
    • 581
    • 582
    • 583
    • 584
    • 585
    • 586
    • 587
    • 588
    • 589
    • 590
    • 591
    • 592
    • 593
    • 594
    • 595
    • 596
    • 597
    • 598
    • 599
    • 600
    • 601
    • 602
    • 603
    • 604
    • 605
    • 606
    • 607
    • 608
    • 609
    • 610
    • 611
    • 612
    • 613
    • 614
    • 615
    • 616
    • 617
    • 618
    • 619
    • 620
    • 621
    • 622
    • 623
    • 624
    • 625
    • 626
    • 627
    • 628
    • 629
    • 630
    • 631
    • 632
    • 633
    • 634
    • 635
    • 636
    • 637
    • 638
    • 639
    • 640
    • 641
    • 642
    • 643
    • 644
    • 645
    • 646
    • 647
    • 648
    • 649
    • 650
    • 651
    • 652
    • 653
    • 654
    • 655
    • 656
    • 657
    • 658
    • 659
    • 660
    • 661
    • 662
    • 663
    • 664
    • 665
    • 666
    • 667
    • 668
    • 669
    • 670
    • 671
    • 672
    • 673
    • 674
    • 675
    • 676
    • 677
    • 678
    • 679
    • 680
    • 681
    • 682
    • 683
    • 684
    • 685
    • 686
    • 687
    • 688
    • 689
    • 690
    • 691
    • 692
    • 693
    • 694
    • 695
    • 696
    • 697
    • 698
    • 699
    • 700
    • 701
    • 702
    • 703
    • 704
    • 705
    • 706
    • 707
    • 708
    • 709
    • 710
    • 711
    • 712
    • 713
    • 714
    • 715
    • 716
    • 717
    • 718
    • 719
    • 720
    • 721
    • 722
    • 723
    • 724
    • 725
    • 726
    • 727
    • 728
    • 729
    • 730
    • 731
    • 732
    • 733
    • 734
    • 735
    • 736
    • 737
    • 738
    • 739
    • 740
    • 741
    • 742
    • 743
    • 744
    • 745
    • 746
    • 747
    • 748
    • 749
    • 750
    • 751
    • 752
    • 753
    • 754
    • 755
    • 756
    • 757
    • 758
    • 759
    • 760
    • 761
    • 762
    • 763
    • 764
    • 765
    • 766
    • 767
    • 768
    • 769
    • 770
    • 771
    • 772
    • 773
    • 774
    • 775
    • 776
    • 777
    • 778
    • 779
    • 780
    • 781
    • 782
    • 783
    • 784
    • 785
    • 786
    • 787
    • 788
    • 789
    • 790
    • 791
    • 792
    • 793
    • 794
    • 795
    • 796
    • 797
    • 798
    • 799
    • 800
    • 801
    • 802
    • 803
    • 804
    • 805
    • 806
    • 807
    • 808
    • 809
    • 810
    • 811
    • 812
    • 813
    • 814
    • 815
    • 816
    • 817
    • 818
    • 819
    • 820
    • 821
    • 822
    • 823
    • 824
    • 825
    • 826
    • 827
    • 828
    • 829
    • 830
    • 831
    • 832
    • 833
    • 834
    • 835
    • 836
    • 837
    • 838
    • 839
    • 840
    • 841
    • 842
    • 843
    • 844
    • 845
    • 846
    • 847
    • 848
    • 849
    • 850
    • 851
    • 852
    • 853
    • 854
    • 855
    • 856
    • 857
    • 858
    • 859
    • 860
    • 861
    • 862
    • 863
    • 864
    • 865
    • 866
    • 867
    • 868
    • 869
    • 870
    • 871
    • 872
    • 873
    • 874
    • 875
    • 876
    • 877
    • 878
    • 879
    • 880
    • 881
    • 882
    • 883
    • 884
    • 885
    • 886
    • 887
    • 888
    • 889
    • 890
    • 891
    • 892
    • 893
    • 894
    • 895
    • 896
    • 897
    • 898

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    mysql基础学习笔记
    OpenCV图像处理——矩形(Rect)类的常用操作
    [ 笔记 ] 计算机网络安全_3_Web安全
    若依微服务版本集成积木报表
    改进的PSO-BP算法在工业机器人末端位姿误差补偿中的应用
    开源 Wiki 软件 wiki.js
    【GET-4】
    亚马逊云科技 Build On 2022 - AIot 第二季物联网专场实验心得
    Linux下的进程控制-进程程序替换
    蓝桥杯:核桃的数量(3148)java
  • 原文地址:https://blog.csdn.net/newlw/article/details/126847259