第三章 二维图元的填充 ( 第一部分: 矩形/多边形/扇形的扫描转换[填充] )

第三章 二维图元的填充

  • 扫描转换矩形
  • 扫描转换多边形
    • 逐点判断法
    • 扫描线算法
    • 边缘填充算法
  • 扫描转换扇形
  • 区域填充(种子填充法)
    • 递归填充算法
    • 扫描线算法
  • 以图像填充区域
  • 字符的表示与输出
扫描转换矩形

矩形作为特殊多边形, 单独处理的原因有

  • 比一般多边形可简化计算
  • 应用非常多, 如窗口系统

填充矩形时的共享边界的处理

原则:左闭右开,下闭上开

即矩形左边、下边的像素属于矩形

void FillRectangle (Rectangle *rect, int color)
{
    int x,y;
    for(y = rect->ymin; y < rect->ymax;y++)
    {
        for(x = rect->xmin;x < rect->xmax;x++)
        {
            PutPixel(x,y,color);
        }
    }
}
扫描转换多边形

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

凸多边形: 任意两顶点间的连线均在多边形内部

凹多边形: 存在一对顶点,它们之间的连线部分或全部不在多边形内部

在这里插入图片描述

逐点判断法

算法思想

  • 找到多边形包围盒, 遍历包围盒中每一个点
    • 如果在多边形中就填充前景色
    • 如果不在就填充背景色

问题有两个

  1. 如果获取多边形包围盒
    • 轴对齐包围盒(Axis-Aligned Bounding Box, AABB)
    • 最小旋转包围盒(Oriented Bounding Box, OBB)
    • 极值投影法(Rotating Calipers)
    • 圆形包围盒(Bounding Circle)
  2. 如何判断点是否在多边形内部
    • 射线法
    • 累计角度法
    • 编码法

这里只介绍一种最常用的求多边形包围盒的方法, AABB 法, 其他的算法可以自行了解

逐点判断法代码示例

#define MAX 100

typedef struct {
    int PolygonNum;           // 多边形顶点个数
    Point vertexces[MAX];     // 多边形顶点数组
} Polygon;                    // 多边形结构

void FillPolygonPbyP(Polygon *P, int polygonColor) {
    int x, y;
    int xmin, xmax, ymin, ymax;					// 找到多边形包围盒
    FindAABB(P, &xmin, &xmax, &ymin, &ymax);
    // 遍历图像包围盒中的所有像素
    for (y = ymin; y < ymax; y++) {
        for (x = xmin; x < xmax; x++) {
            if (IsInside(P, x, y)) {
                PutPixel(x, y, polygonColor);  	// 在多边形内部,画前景色
            } else {
                PutPixel(x, y, backgroundColor);// 在多边形外部,画全局背景色
            }
        }
    }
}
轴对齐包围盒(Axis-Aligned Bounding Box, AABB)

初始化 xmin, xmax, ymin, ymax,并设置为多边形第一个顶点的坐标

遍历多边形的所有顶点:

  • 更新 xmin 为最小的 x 坐标
  • 更新 xmax 为最大的 x 坐标
  • 更新 ymin 为最小的 y 坐标
  • 更新 ymax 为最大的 y 坐标

xmin, xmax, ymin, ymax 就是包围盒的边界

void FindAABB(Polygon *P, int *xmin, int *xmax, int *ymin, int *ymax) {
    *xmin = *xmax = P->vertexces[0].x;
    *ymin = *ymax = P->vertexces[0].y;

    for (int i = 1; i < P->PolygonNum; i++) {
        if (P->vertexces[i].x < *xmin) *xmin = P->vertexces[i].x;
        if (P->vertexces[i].x > *xmax) *xmax = P->vertexces[i].x;
        if (P->vertexces[i].y < *ymin) *ymin = P->vertexces[i].y;
        if (P->vertexces[i].y > *ymax) *ymax = P->vertexces[i].y;
    }
}
  • 优点:

    • 简单快速:该算法时间复杂度为 O(n),其中 n 是多边形顶点的数量。

    • 适用于任意形状的多边形:对于任意形状的多边形都适用。

  • 缺点:

    • 不精确:因为包围盒的边总是与坐标轴对齐,对于旋转的多边形或非对齐的多边形,AABB 可能包含了更多的空白区域。
射线法

算法步骤

  1. 从待判别点v发出射线
  2. 求交点个数 k
  3. k 的奇偶性决定了点与多边形的内外关系
    • 奇数个交点, 点在多边形内部
    • 偶数个交点, 点在多边形外部

缺陷:

  • 射线通过顶点
    • 顶点连接两条边,当射线穿过顶点时,射线既会与顶点连接的其中一条边相交,又会与另一条边相交。如果不做特殊处理,可能会将这个顶点算作两个交点,从而错误地增加交点数量
      • 为了解决这个问题,常见的做法是忽略射线与顶点的交点,或根据相邻两条边的位置进行适当的调整,从而确保交点数量的准确性
  • 对于边自交的多边形会产生误判, 如下图中 V0 在多边形内部却被判定为多边形外部

在这里插入图片描述

累计角度法

算法步骤

  1. 从v点向多边形P顶点发出射线,形成有向角
  2. 计算有向角的和,得出结论

问题

三角函数的周期性会导致夹角的表示不唯一,从而影响累计角度法的准确性

  • 通过将每个夹角限制在 (−π,π] 范围内,可以确保累计角度准确反映点相对于多边形的内部或外部情况

∑ i = 1 n θ i = { 0  , 点 V 位于多边形外 ± 2 π  , 点 V 位于多边形内 \sum_{i=1}^{n} \theta_i= \begin{cases} 0 \text{ , 点 V 位于多边形外} \\ \pm2\pi \text{ , 点 V 位于多边形内} \end{cases} i=1nθi={0 ,  V 位于多边形外±2π ,  V 位于多边形内

在这里插入图片描述

编码法

编码法是累计角度法的 离散方法

算法步骤

  1. 预处理,测试点在边上否?若是,判别结束。

  2. V 为原点作局部坐标系,象限按逆时针(或顺时针)编码

  3. 对各顶点进行编码,Pi 编码为象限编码 Ii

  4. 对各边进行编码,记为
    △ P i P i + 1 = I p i + 1 − I p i △P_iP_{i+1}=I{p_{i+1}}-I_{pi} PiPi+1=Ipi+1Ipi
    △PiPi+1 的周期为4,值在 (-2,2]

  5. 求边编码的和, 其中 △PnPn+1 = △PnP0
    ∑ i = 0 n Δ P i P i + 1 = { 0  , 点 V 位于多边形外 ± 4  , 点 V 位于多边形内 \sum_{i=0}^n \Delta P_iP_{i+1}= \begin{cases} 0 \text{ , 点 V 位于多边形外} \\ \pm4 \text{ , 点 V 位于多边形内} \end{cases} i=0nΔPiPi+1={0 ,  V 位于多边形外±4 ,  V 位于多边形内

在这里插入图片描述

  • 优点
    • 程序简单
  • 缺点
    • 效率低
      • 没有利用相邻像素之间的连贯性
扫描线算法

目标

  • 利用相邻像素之间的连贯性,提高算法效率

处理对象

  • 非自交多边形 (边与边之间除了顶点外无其它交点)

基本原理

  • 一条扫描线与多边形的边有偶数个交点

算法步骤 ( 水平扫描线 )

  1. 求交:计算扫描线与多边形各边的交点
  2. 排序:把所有交点按 x 值递增顺序排序
  3. 配对:第一个与第二个, 第三个与第四个等等, 每对交点代表扫描线与多边形的一个相交区间
  4. 填色:把相交区间内的象素置成多边形颜色,把相交区间外的象素置成背景色

算法思想

边的连贯性

相邻扫描线与边的交点的坐标有连贯性, 计算方法与直线光栅化思想一致, 用DDA算法

即第 i 条扫描线与多边形某边 Ej 的交点为 Xij, 第 i+1 条扫描线与多边形某边 Ej 的交点为 X(i+1)j, 边的斜率为 kj, 则有
x ( i + 1 ) j = x i j + 1 k j x_{{(i+1)}j}=x_{ij}+\frac 1 k_j x(i+1)j=xij+k1j

将扫描线上的交点分为两类

  • 交点是某条边的 上一交点的后继
    • 此时可以套用上述公式
  • 交点是某条边的 起始端点
    • 此时边的下端点就是交点, 无需计算
// 定义边的数据结构为
typedef struct{
    int ymax; 
    float x, deltax; 
    struct Edge *nextEdge;
} Edge;
// ymax 边的上端点y坐标
// x 初值为边下端点的x坐标,AEL中为当前扫描线与边的交点的X坐标;
// deltax 边的斜率的倒数
// nextEdge 指向下一条边的指针

为了跟踪当前扫描线和多边形的交点,以确定该行哪些像素应该被填充, 提出以下两个概念

  • 边的分类表 (ET)
  • 活性边表 (AEL)
边的分类表 (ET)

按照边的下端点 y 坐标 对非水平边进行分类的指针数组

  • 下端点 y 坐标值等于 i 的边属于第 i
  • 同一类中的边按 x 值 ( x 相等的按 deltax 排)递增顺序排列。

以下图多边形为例

在这里插入图片描述

该多边形的 ET 如下图

在这里插入图片描述

活性边表 (AEL)

当处理一条扫描线时,仅对多边形与它相交的边进行求交运算

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

对于上面提到的多边形, y=6y=7 两条扫描线对应的 AEL 如下

在这里插入图片描述

算法步骤

  1. 建立ET
  2. 将扫描线纵坐标 y 的初值置为ET中非空元素的最小序号,如在上图中,y=1
  3. 置AEL为空
  4. 执行下列步骤直至ET和AEL都为空
    • 如ET中的第 y 类非空,则将其中的所有边取出并插入AEL中
    • 如果有新边插入AEL,则对AEL中各边排序
    • 对AEL中的边两两配对,(1和2为一对,3和4为一对,…),将每对边中 x 坐标按规则取整,获得有效的填充区段,再填充
    • 将当前扫描线纵坐标 y 值递增 1
    • 将AEL中满足 y=ymax 的边删去, 因为每条边被看作下闭上开的)
    • 对AEL中剩下的每一条边的x递增自身的 deltax 值,即 x = x+deltax
边缘填充算法

光栅图形中,如果某区域已填上颜色值 M,当做偶数次求余运算,该区域颜色不变;做奇数次求余运算,则该区域颜色变为值为 M余 的颜色

实际上色中可以在 背景色目标色 中来回切换实现类似效果

这一规律应用于多边形扫描转换,就为边缘填充算法

  • 对于每条扫描线和每条多边形边的交点,将该扫描线上交点右方的所有象素取余
  1. 扫描线为中心的边缘填充算法

    1. 将当前扫描线的所有像素着色为 背景色
    2. 对于每条扫描线和每条多边形边的交点,将该扫描线上交点右方的所有象素
      • 若为第 奇数 个交点, 取 目标色
      • 若为第 偶数 个交点, 取 背景色
    • 这里的每个交点都是不用排序的, 交点的顺序对结果没有影响, 但由于求交点是从左往右的, 一般也是从左向右来处理

在这里插入图片描述

  1. 边为中心的边缘填充算法
    • 将绘图窗口的背景色置为 M余
    • 对多边形的每一条非水平边做:从该边上的每个象素开始向右求余

在这里插入图片描述

  • 优点
    • 算法简单
  • 缺点
    • 对于复杂图形,每一象素可能被访问多次,输入/输出的量比扫描线算法大得多,造成速度较慢
  • 适合用于具有帧缓存的图形系统, 提前处理后, 按扫描线顺序读出帧缓存的内容,送入显示设备
扫描转换扇形

扇形区域的描述

  • 半径,起始角,终止角 ( 圆心不在原点的, 通过坐标转换到原点再处理 )

原理:同扫描转换多边形 ( 圆弧与扫描线交点直接带入 y 值算出来 )

问题:如何确定扫描线与直线段和圆弧段的相交顺序

  • 对不同情况分类

在这里插入图片描述

令点P1,P2满足:θ1 < θ2

按P1和P2所处象限的不同,需要分成 4×4=16种 情况来讨论

先讨论 P1 在第一象限的四种情况

  1. P2 在第一象限

    • 0 < Y < Yp1 时左侧是扫描线与直线段 OP2 的交点, 右侧是与直线段 OP1 的交点
    • Yp1 < Y < Yp2 时左侧是扫描线与直线段 OP2 的交点, 右侧是与圆弧 P1P2 的交点

    如下图

在这里插入图片描述

  1. P2 在第二象限

    分为两种情况

    • Yp1 < Yp2
    • Yp1 > Yp2

在这里插入图片描述 在这里插入图片描述

  1. P2 在第三象限

在这里插入图片描述

  1. P2 在第四象限

在这里插入图片描述

当 P1 在其它象限时

  • 将扇形顺时针转动, 使 P1 落在第一象限内, 扫描完成后直接将整个扇形逆时针旋转回原来的位置

扫描扇形的其他方法

用多边形逼近扇形的圆弧部分, 再用扫描多边形的算法来填充扇形

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值