多边形扫描线填充算法

1.基本思想

        按扫描线顺序,计算扫描线与多边形的相交区间,再用要求的颜色显示这些区间的象素,即完成填充工作。

         对于一条扫描线填充过程可以分为四个步骤:

        1. 求交:计算扫描线与多边形各边的交点;

        2.排序:把所有交点按x值递增顺序排序;

        3. 配对:第一个与第二个,第三个与第四个等等;每对交点代表扫描线与多边形的一个相交区间,

        4.着色:把相交区间内的象素置成多边形颜色,把相交区间外的象素置成背景色。

2.算法过程

        1、根据给出的多边形顶点坐标,建立NET表;求出顶点坐标中最大y值ymax和最小y值ymin。

        2、初始化AET表指针,使它为空。

        3、执行下列步骤直至NET和AET都为空.    

                3.1、如NET中的第y类非空,则将其中的所有边取出并插入AET中;    

                3.2、如果有新边插入AET,则对AET中各边排序;    

                3.3、对AET中的边两两配对,(1和2为一对,3和4为一对,…),将每对边中x坐标按规则取整,获得有效的填充区段,再填充.    

                3.4、将当前扫描线纵坐标y值递值1;    

                3.5、如果AET表中某记录的ymax=yj,则删除该记录            (因为每条边被看作下闭上开的);    

                3.6、对AET中剩下的每一条边的x递增1/k,即x = x+ 1/k .

3.重要操作

        1.新边表

        (1)用来对除水平边外的所有边进行登记, 来建立边的记录。边的记录定义为(x, delta_x, y_max)

        (2)将每条边的信息链入与该边最小y坐标(ymin )相对应的桶处。也就是说,若某边的较低端点为ymin,则该边就放在相应的扫描线桶中。

     (3)每条边的数据形成一个结点,内容包括:该扫描线与该边的初始交点x(即较低端点的x值), 1/k,以及该边的最大y值ymax。

       (4)同一桶中若干条边按X|ymin由小到大排序,若X|ymax 相等,则按照1/k由小到大排序。(这里其实有问题,当两条边起点x相同,且方向向着x减小,y增大时,应该是由大到小排序,但是在活动边表的构建时,因为当y增大时,x同样会发生变化,问题就会迎刃而解)

        2.活动边表

        (1)NET表建立以后,就可以开始扫描转换了。对不同的扫描线,与之相交的边线也是不同的,当对某一条扫描线进行扫描转换时,我们只需要考虑与它相交的那些边线,为此需要建立一个只与当前扫描线相交的边记录链表,称之为活动边表。

多边形扫描线填充算法_第1张图片

4.代码实现

import tkinter as tk
class Node:
    def __init__(self, x=None, delta=None, y=None):
        self.x = x
        self.delta = delta
        self.y = y

    def __repr__(self):
        return f"({self.x}, {self.delta}, {self.y})"

def bucket(list):
    if not list:
        return {}

    y_min = list[0][1]
    y_max = list[0][1]

    for dot in list[1:]:
        y_min = min(y_min, dot[1])
        y_max = max(y_max, dot[1])

    return {i: [] for i in range(y_min, y_max+1)}

def insert(head, new_node):
    if len(head) == 0:
        head.append(new_node)
    else:
        for i in range(len(head)):
            if new_node.x < head[i].x:
                head.insert(i, new_node)
                break
            elif new_node.x == head[i].x:
                if new_node.delta > head[i].delta:
                    head.insert(i+1, new_node)
                else:
                    head.insert(i, new_node)
                break
            elif new_node.x > head[i].x:
                head.insert(i+1, new_node)
                break
    return head

def insert_AET(head, new_node):
    if len(head) == 0:
        head.append(new_node)
    else:
        inserted = False
        for i in range(len(head)):
            if new_node.x < head[i].x or (new_node.x == head[i].x and new_node.delta <= head[i].delta):
                head.insert(i, new_node)
                inserted = True
                break
        if not inserted:
            head.append(new_node)
    return head

def NET(list, bucket_1: dict):
    for i in range(len(list)):
        x1, y1 = list[i]
        x2, y2 = list[(i+1) % len(list)]
        if y1 < y2:
            new_node = Node(x1, (x2-x1)/(y2-y1), y2)
            bucket_1[y1] = insert(bucket_1[y1], new_node)
        elif y1 > y2:
            new_node = Node(x2, (x1 - x2) / (y1 - y2), y1)
            bucket_1[y2] = insert(bucket_1[y2], new_node)
        else:
            new_node = Node(x2, (x1 - x2), y1)
            bucket_1[y2] = insert(bucket_1[y2], new_node)
    return bucket_1

def AET(NET:dict, bucket_2):
    AET = bucket_2
    for current_y, edges in NET.items():
        for side in edges:
            y_max = side.y
            x = side.x
            delta = side.delta
            for i in range(current_y, y_max):
                AET[i] = insert_AET(AET[i], Node(x, delta, y_max))
                x += delta
    return AET

def fill(canvas, AET):
    for current_y, edges in AET.items():
        for i in range(int(len(edges)/2)):
            for x in range(int(edges[2*i].x), int(edges[2*i+1].x)):
                canvas.create_oval(x, current_y, x+1, current_y+1, fill='black')

list = [(200, 200), (300, 300), (400, 200), (300, 400)]
bucket_1 = bucket(list)
bucket_2 = bucket(list)
NET = NET(list, bucket_1)
AET = AET(NET, bucket_2)


window = tk.Tk()
window.title('scan')
window.geometry('800x500+400+200')

canvas = tk.Canvas(window, bg='white', height=500, width=800)
canvas.grid()
fill(canvas, AET)

window.mainloop()

你可能感兴趣的:(算法,python)