多边形扫描转换算法

没有简介就是最好的简介🙃

概述

多边形有两种重要的表示方法:

  • 顶点表示

用多边形的顶点序列来表示多边形,这种表示直观、几何意义强、占内存少,易于进行几何变换

但是因为它没有明确指出哪些像素在多边形内,所以不能直接用于面着色

00.png

  • 点阵表示

用位于多边形内部的像素集合来刻画多边形,这种表示丢失了许多几何信息(eg. 边界、顶点 etc.),但它却是光栅显示系统显示时所需的表示形式

01.png

把多边形的顶点表示转换为点阵表示,这种转换就称之为多边形的扫描转换

多边形分为:凸多边形、凹多边形、含内环的多边形 etc.

  1. 凸多边形:多边形内任意两点间的连线均在多边形内部
  2. 凹多边形:多边形内任意两点间的连线存在不在多边形内部的
  3. 含内环的多边形:多边形内包含多边形

多边形扫描转换要解决的问题是:在知道多边形的边界时,找到多边形内部的点,即把多边形内部填上颜色

X-扫描线算法

基本思想:按照扫描线顺序,计算扫描线与多边形的相交区间,再用要求的颜色显示这些区间里的像素

算法的核心:按照 x 递增顺序排列交点的 x 坐标序列

步骤:

  1. 确定多边形所占有的最大扫描线数量,得到多边形顶点的最大值和最小值($y_{max}$ 和 $y_{min}$)
  2. 从 $y = y_{min}$ 到 $y = y_{max}$,每次用一条扫描线进行填充,又分为如下四个步骤:
    1. 求交:计算扫描线与多边形各边的交点
    2. 排序:把所有交点按递增顺序进行排序
    3. 配对:把交点根据区间进行配对(交点必须是偶数个,第1、2个交点一对,第3、4个交点一对)
    4. 区间填色:把这些相交区间内的像素置成不同于背景色的填充色

当扫描线与多边形顶点相交时,交点的取舍问题:

  1. 若共享顶点的两条边分别在扫描线的两边,交点只算一个
  2. 若共享顶点的两条边在扫描线的同一边,交点算零个或者两个

如何判断共享顶点的两条边的位置:检查共享顶点的两条边的另外两个端点的 y 值,按这两个 y 值中大于交点 y 值的个数来决定交点数(个数为几交点就为几)

为了计算每条扫描线与多边形各边的交点,最简单的方法是把多边形的所有边放在一个表中,在处理每条扫描线的时候,按顺序从表中间取出所有的边,分别与扫描线求交

但是!!!这个算法的效率非常低,因为求交运算的计算量十分大

改进 X-扫描线算法

最理想的算法是不求交

扫描转换算法提出的重要思想:

  1. 扫描线:当处理图形图像时按一条条扫描线处理
  2. 增量的思想

在求交点时使用增量,因为每条扫描线的 y 值都是知道的,所以关键的是求 x 的值

改进方案:

1. 在处理一条扫描线时,仅对与它相交的多边形的边(有效边)进行求交运算

eg. 如图,只计算扫描线与 $P_{1}P_{4}$ 和 $P_{2}P_{3}$ 的交点

02.png

2. 考虑扫描线的连贯性

即当前扫描线与各边的交点顺序与下一条扫描线与各边的交点顺序很可能相同或非常相似

3. 多边形的连贯性

即当某条边与当前扫描线相交的时候,它很可能也与下一条扫描线相交

为了避免求交运算,需要引进一套特殊的数据结构:

1. 活性边表(AET):把与当前扫描线相交的边称为活性边,并把它们按与扫描线交点 x 坐标递增的顺序存放在一个链表里

2. 节点内容(一个节点在数据结构中可以用结构来表示):

$x$:当前扫描线与边的交点坐标

$\Delta x$:从当前扫描线到下一条扫描线之间 x 的增量

$y_{max}$:该边所交的最高扫描线的 y 坐标值 $y_{max}$

next:指向下一条边的指针

$x$ $\Delta x$ $y_{max}$ next

随着扫描线的移动,扫描线与多边形的交点与上一次交点相关:

03.png

设边的直线斜率为 $k$:

$$k = \frac{\Delta y}{\Delta x} = \frac{y_{i+1} - y_{i}}{x_{i+1} - x_{i}}$$

$$\because y_{i+1} - y_{i} = 1$$

$$\therefore x_{i+1} - x_{i} = \frac{1}{k}$$

$$\Rightarrow x_{i+1} = x_{i} + \frac{1}{k}$$

这里的 $\frac{1}{k}$ 就是增量,即:$\Delta x = \frac{1}{k}$

另外,需要知道一条边何时不再与下一条扫描线相交,以便及时把它从有效边表里删除出去,避免下一步进行无谓的计算

04.png

为了方便活性边表的建立与更新,需要构造一个新边表(NET),用来存放多边形的边的信息,有四个步骤:

使用如图的多边形举例:

05.png

1. 构造一个纵向链表,链表的长度为多边形所占有的最大扫描线数,链表的每个节点,称为一个吊桶,对应多边形覆盖的每一条扫描线

06.png

2. NET 挂在与该边低端 y 值相同的扫描线桶中,即:存放在该扫描线第一次出现的边

  • 该边的 $y_{max}$
  • 该边较低点的 x 坐标值 $x_{min}$
  • 该边的斜率 $\frac{1}{k}$
  • 指向下一条具有相同较低端 y 坐标的边的指针
$y_{max}$ x_{min} $\frac{1}{k}$ next

07.png

通过上面的 NET 表就可以知道多边形是从哪里开始的:在这个表中只有 1、3、5、7 有边存在,而在 1 这条扫描线有两条边进入,所以就把这两条边放进活性边表进行处理

每做一次新的扫描线时,需要对已有的边进行三个处理:

  1. 判断是否是活性边,不是就除掉
  2. 如果没有被除掉,就要对它的数据进行更新,即:更新它的 x 值($x + \frac{1}{k}$)
  3. 判断有没有新的边进入,有新的边进入 NET 表里,可以插入排序

扫描线算法可以实现已知任意多边形区域边界的填充

该填充算法是按照扫描线的顺序,计算扫描线与待填充区域的相交区间,再用要求的颜色显示这些区间里的像素

为了提高算法效率:

  1. 使用增量的思想
  2. 使用连贯性的思想
  3. 构建了一套特殊的数据结构

这里区间的端点是通过计算扫描线与多边形边界的交点获得的,所以待填充区域的边界线必须是已知的,因此:

X-扫描线算法的缺点是:无法实现对未知边界的区域进行填充