按扫描线顺序,计算扫描线与多边形的相交区间,再用要求的颜色显示这些区间的象素,即完成填充工作。
对于一条扫描线填充过程可以分为四个步骤:
1. 求交:计算扫描线与多边形各边的交点;
2.排序:把所有交点按x值递增顺序排序;
3. 配对:第一个与第二个,第三个与第四个等等;每对交点代表扫描线与多边形的一个相交区间,
4.着色:把相交区间内的象素置成多边形颜色,把相交区间外的象素置成背景色。
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 .
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表建立以后,就可以开始扫描转换了。对不同的扫描线,与之相交的边线也是不同的,当对某一条扫描线进行扫描转换时,我们只需要考虑与它相交的那些边线,为此需要建立一个只与当前扫描线相交的边记录链表,称之为活动边表。
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()