本文原创首发于我的个人博客
为了更好的阅读体验,建议原站阅读
多边形有两种重要的表示方法:
用多边形的顶点序列来表示多边形,这种表示直观、几何意义强、占内存少,易于进行几何变换
但是因为它没有明确指出哪些像素在多边形内,所以不能直接用于面着色
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oMmotAe4-1586768398994)(https://cdn.jsdelivr.net/gh/TUFZ/ImgHosting//TUFZ-Img/article/20Apr13A/00.png)]
用位于多边形内部的像素集合来刻画多边形,这种表示丢失了许多几何信息(eg. 边界、顶点 etc.),但它却是光栅显示系统显示时所需的表示形式
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Z3WKXOJB-1586768398998)(https://cdn.jsdelivr.net/gh/TUFZ/ImgHosting//TUFZ-Img/article/20Apr13A/01.png)]
把多边形的顶点表示转换为点阵表示,这种转换就称之为多边形的扫描转换
多边形分为:凸多边形、凹多边形、含内环的多边形 etc.
多边形扫描转换要解决的问题是:在知道多边形的边界时,找到多边形内部的点,即把多边形内部填上颜色
基本思想:按照扫描线顺序,计算扫描线与多边形的相交区间,再用要求的颜色显示这些区间里的像素
算法的核心:按照 x 递增顺序排列交点的 x 坐标序列
步骤:
当扫描线与多边形顶点相交时,交点的取舍问题:
如何判断共享顶点的两条边的位置:检查共享顶点的两条边的另外两个端点的 y 值,按这两个 y 值中大于交点 y 值的个数来决定交点数(个数为几交点就为几)
为了计算每条扫描线与多边形各边的交点,最简单的方法是把多边形的所有边放在一个表中,在处理每条扫描线的时候,按顺序从表中间取出所有的边,分别与扫描线求交
**但是!!!**这个算法的效率非常低,因为求交运算的计算量十分大
最理想的算法是不求交
扫描转换算法提出的重要思想:
在求交点时使用增量,因为每条扫描线的 y 值都是知道的,所以关键的是求 x 的值
改进方案:
1. 在处理一条扫描线时,仅对与它相交的多边形的边(有效边)进行求交运算
eg. 如图,只计算扫描线与 P 1 P 4 P_{1}P_{4} P1P4 和 P 2 P 3 P_{2}P_{3} P2P3 的交点
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vwGUKDzp-1586768399000)(https://cdn.jsdelivr.net/gh/TUFZ/ImgHosting//TUFZ-Img/article/20Apr13A/02.png)]
2. 考虑扫描线的连贯性
即当前扫描线与各边的交点顺序与下一条扫描线与各边的交点顺序很可能相同或非常相似
3. 多边形的连贯性
即当某条边与当前扫描线相交的时候,它很可能也与下一条扫描线相交
为了避免求交运算,需要引进一套特殊的数据结构:
1. 活性边表(AET):把与当前扫描线相交的边称为活性边,并把它们按与扫描线交点 x 坐标递增的顺序存放在一个链表里
2. 节点内容(一个节点在数据结构中可以用结构来表示):
x x x:当前扫描线与边的交点坐标
Δ x \Delta x Δx:从当前扫描线到下一条扫描线之间 x 的增量
y m a x y_{max} ymax:该边所交的最高扫描线的 y 坐标值 y m a x y_{max} ymax
next:指向下一条边的指针
x x x | Δ x \Delta x Δx | y m a x y_{max} ymax | next |
---|
随着扫描线的移动,扫描线与多边形的交点与上一次交点相关:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hMLIDibh-1586768399001)(https://cdn.jsdelivr.net/gh/TUFZ/ImgHosting//TUFZ-Img/article/20Apr13A/03.png)]
设边的直线斜率为 k k k:
k = Δ y Δ x = y i + 1 − y i x i + 1 − x i k = \frac{\Delta y}{\Delta x} = \frac{y_{i+1} - y_{i}}{x_{i+1} - x_{i}} k=ΔxΔy=xi+1−xiyi+1−yi
∵ y i + 1 − y i = 1 \because y_{i+1} - y_{i} = 1 ∵yi+1−yi=1
∴ x i + 1 − x i = 1 k \therefore x_{i+1} - x_{i} = \frac{1}{k} ∴xi+1−xi=k1
⇒ x i + 1 = x i + 1 k \Rightarrow x_{i+1} = x_{i} + \frac{1}{k} ⇒xi+1=xi+k1
这里的 1 k \frac{1}{k} k1 就是增量,即: Δ x = 1 k \Delta x = \frac{1}{k} Δx=k1
另外,需要知道一条边何时不再与下一条扫描线相交,以便及时把它从有效边表里删除出去,避免下一步进行无谓的计算
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Rj5lUHI7-1586768399002)(https://cdn.jsdelivr.net/gh/TUFZ/ImgHosting//TUFZ-Img/article/20Apr13A/04.png)]
为了方便活性边表的建立与更新,需要构造一个新边表(NET),用来存放多边形的边的信息,有四个步骤:
使用如图的多边形举例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PQUMehe3-1586768399002)(https://cdn.jsdelivr.net/gh/TUFZ/ImgHosting//TUFZ-Img/article/20Apr13A/05.png)]
1. 构造一个纵向链表,链表的长度为多边形所占有的最大扫描线数,链表的每个节点,称为一个吊桶,对应多边形覆盖的每一条扫描线
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dZ6O3fnF-1586768399003)(https://cdn.jsdelivr.net/gh/TUFZ/ImgHosting//TUFZ-Img/article/20Apr13A/06.png)]
2. NET 挂在与该边低端 y 值相同的扫描线桶中,即:存放在该扫描线第一次出现的边
y m a x y_{max} ymax | x_{min} | 1 k \frac{1}{k} k1 | next |
---|
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IhzZpNaC-1586768399004)(https://cdn.jsdelivr.net/gh/TUFZ/ImgHosting//TUFZ-Img/article/20Apr13A/07.png)]
通过上面的 NET 表就可以知道多边形是从哪里开始的:在这个表中只有 1、3、5、7 有边存在,而在 1 这条扫描线有两条边进入,所以就把这两条边放进活性边表进行处理
每做一次新的扫描线时,需要对已有的边进行三个处理:
扫描线算法可以实现已知任意多边形区域边界的填充
该填充算法是按照扫描线的顺序,计算扫描线与待填充区域的相交区间,再用要求的颜色显示这些区间里的像素
为了提高算法效率:
这里区间的端点是通过计算扫描线与多边形边界的交点获得的,所以待填充区域的边界线必须是已知的,因此:
**X-扫描线算法的缺点是:**无法实现对未知边界的区域进行填充