1. 什么是扫描线填充算法?
扫描线(Scanline)填充算法是一种用于绘制二维图形的计算机图形学算法。它基于扫描线的概念,沿一个水平线逐个像素地扫描,并确定每个像素的颜色和其他属性。当扫描到一个形状边界时,该算法可以确定该形状的边界,并在内部填充该形状。
1.1 扫描线算法的实现流程
扫描线算法可以分为三步:
创建扫描线(视为无限长,但只扫描在多边形内的部分)并将其按照 y 坐标上升的顺序排序。
逐个扫描扫描线的像素,确定该像素的颜色和其他属性。
当扫描线到达多边形的边界时,停止当前扫描线的扫描并关闭扫描线。
2. 扫描线填充算法的优点
相比于其他绘制多边形的算法,如边界追踪算法和区域增长算法,扫描线填充算法有以下优点:
填充效果好,不会出现漏填漏洞的情况。
相对于边界追踪算法,不需要边界的信息即可进行填充。
3. 扫描线填充算法的实现
3.1 前置知识:建立边表和活性边表
扫描线填充算法的实现需要建立边表和活性边表。边表用于存储多边形的边界信息,活性边表则是在扫描线逐个像素扫描的过程中用于存储当前扫描线与多边形相交的边。
边表的建立方法:
# Edge 类用于表示多边形的一条边
class Edge:
def __init__(self, y_max, x_min, k_recip, edge_id):
self.y_max = y_max # 边的最高点
self.x_min = x_min # 边在该最高点下的最小 x 坐标
self.k_recip = k_recip # 边的倾斜率的倒数
self.edge_id = edge_id # 边的编号
# 建立多边形的边表
def build_edge_table(vertices):
num_vertices = len(vertices)
edge_table = [[] for i in range(num_vertices)]
for i in range(num_vertices):
v0 = vertices[i]
v1 = vertices[(i+1) % num_vertices]
if v0[1] == v1[1]: # 忽略水平排列的边界线
continue
if v0[1] > v1[1]: # 确保 y1 > y0
v0, v1 = v1, v0
k_recip = (v1[0] - v0[0]) / (v1[1] - v0[1])
edge = Edge(v1[1], v0[0], k_recip, i)
edge_table[v0[1]].append(edge)
return edge_table
活性边表的建立方法:
# ActiceEdge 类用于表示活性边表中的一条边
class ActiveEdge:
def __init__(self, edge, y_min):
self.edge = edge # 对应的边
self.y_min = y_min # 边所在的扫描线的 y 坐标
# 根据当前扫描线和边表建立活性边表
def build_active_edge_table(scan_line, edge_table):
active_edges = []
for edge in edge_table[scan_line]:
active_edge = ActiveEdge(edge, scan_line)
active_edges.append(active_edge)
return active_edges
3.2 实现代码
下面是使用 Python 进行扫描线填充算法的具体代码实现,其实现了上述的建表步骤和算法实现步骤。
def fill_polygon(vertices, color):
# 建立边表和活性边表
edge_table = build_edge_table(vertices)
active_edges = []
# 寻找多边形的上下边界
y_min = min(vertices, key=lambda x: x[1])[1]
y_max = max(vertices, key=lambda x: x[1])[1]
# 扫描线逐个扫描
for y in range(y_min, y_max+1):
# 添加新的边进入活性边表
active_edges.extend(build_active_edge_table(y, edge_table))
# 按照 x 坐标排列活性边
active_edges.sort(key=lambda x: x.edge.x_min)
# 按照 2x 坐标递增的顺序填充像素
for i in range(0, len(active_edges), 2):
x0 = int(active_edges[i].edge.x_min)
x1 = int(active_edges[i+1].edge.x_min)
for x in range(x0, x1+1):
set_pixel(x, y, color)
# 将当前扫描线下的边从活性边表中删除
active_edges = [edge for edge in active_edges if edge.edge.y_max != y]
# 更新活性边表中边的 x 坐标位置
for edge in active_edges:
edge.edge.x_min += edge.edge.k_recip
# 设置像素颜色
def set_pixel(x, y, color):
pass
4. 总结
扫描线填充算法是一种有效的绘制二维图形的算法,在计算机图形学中应用广泛。通过建立边表和活性边表的方式,该算法能够快速且准确地确定多边形的边界,并将其填充。其实现简单,填充效果好,是一种值得掌握的算法。