算法系列之十二:多边形区域填充算法--扫描线填充算法(有序边表法)

二、扫描线算法(Scan-Line Filling)

        扫描线算法适合对矢量图形进行区域填充,只需要直到多边形区域的几何位置,不需要指定种子点,适合计算机自动进行图形处理的场合使用,比如电脑游戏和三维CAD软件的渲染等等。

        对矢量多边形区域填充,算法核心还是求交。《计算几何与图形学有关的几种常用算法》一文给出了判断点与多边形关系的算法――扫描交点的奇偶数判断算法,利用此算法可以判断一个点是否在多边形内,也就是是否需要填充,但是实际工程中使用的填充算法都是只使用求交的思想,并不直接使用这种求交算法。究其原因,除了算法效率问题之外,还存在一个光栅图形设备和矢量之间的转换问题。比如某个点位于非常靠近边界的临界位置,用矢量算法判断这个点应该是在多边形内,但是光栅化后,这个点在光栅图形设备上看就有可能是在多边形外边(矢量点没有大小概念,光栅图形设备的点有大小概念),因此,适用于矢量图形的填充算法必须适应光栅图形设备。

2.1扫描线算法的基本思想

        扫描线填充算法的基本思想是:用水平扫描线从上到下(或从下到上)扫描由多条首尾相连的线段构成的多边形,每根扫描线与多边形的某些边产生一系列交点。将这些交点按照x坐标排序,将排序后的点两两成对,作为线段的两个端点,以所填的颜色画水平直线。多边形被扫描完毕后,颜色填充也就完成了。扫描线填充算法也可以归纳为以下4个步骤:

(1)       求交,计算扫描线与多边形的交点

(2)       交点排序,对第2步得到的交点按照x值从小到大进行排序;

(3)       颜色填充,对排序后的交点两两组成一个水平线段,以画线段的方式进行颜色填充;

(4)       是否完成多边形扫描?如果是就结束算法,如果不是就改变扫描线,然后转第1步继续处理;

        整个算法的关键是第1步,需要用尽量少的计算量求出交点,还要考虑交点是线段端点的特殊情况,最后,交点的步进计算最好是整数,便于光栅设备输出显示。

        对于每一条扫描线,如果每次都按照正常的线段求交算法进行计算,则计算量大,而且效率底下,如图(6)所示:

图(6) 多边形与扫描线示意图

观察多边形与扫描线的交点情况,可以得到以下两个特点:

(1)       每次只有相关的几条边可能与扫描线有交点,不必对所有的边进行求交计算;

(2)       相邻的扫描线与同一直线段的交点存在步进关系,这个关系与直线段所在直线的斜率有关;

        第一个特点是显而易见的,为了减少计算量,扫描线算法需要维护一张由“活动边”组成的表,称为“活动边表(AET)”。例如扫描线4的“活动边表”由P1P2和P3P4两条边组成,而扫描线7的“活动边表”由P1P2、P6P1、P5P6和P4P5四条边组成。

        第二个特点可以进一步证明,假设当前扫描线与多边形的某一条边的交点已经通过直线段求交算法计算出来,得到交点的坐标为(x, y),则下一条扫描线与这条边的交点不需要再求交计算,通过步进关系可以直接得到新交点坐标为(x + △x, y + 1)。前面提到过,步进关系△x是个常量,与直线的斜率有关,下面就来推导这个△x。

        假设多边形某条边所在的直线方程是:ax + by + c = 0,扫描线yi和下一条扫描线yi+1与该边的两个交点分别是(xi,yi)和(xi+1,yi+1),则可得到以下两个等式:

axi + byi + c = 0                        (等式 1)

axi+1 + byi+1 + c = 0                     (等式 2)

由等式1可以得到等式3:

xi = -(byi + c) / a                           (等式 3)

同样,由等式2可以得到等式4:

xi+1 = -(byi+1 + c) / a                      (等式 4)

由等式 4 – 等式3可得到

xi+1 – xi = -b (yi+1 - yi) / a

由于扫描线存在yi+1 = yi + 1的关系,将代入上式即可得到:

xi+1 – xi = -b / a

即△x = -b / a,是个常量(直线斜率的倒数)。

        “活动边表”是扫描线填充算法的核心,整个算法都是围绕者这张表进行处理的。要完整的定义“活动边表”,需要先定义边的数据结构。每条边都和扫描线有个交点,扫描线填充算法只关注交点的x坐标。每当处理下一条扫描线时,根据△x直接计算出新扫描线与边的交点x坐标,可以避免复杂的求交计算。一条边不会一直待在“活动边表”中,当扫描线与之没有交点时,要将其从“活动边表”中删除,判断是否有交点的依据就是看扫描线y是否大于这条边两个端点的y坐标值,为此,需要记录边的y坐标的最大值。根据以上分析,边的数据结构可以定义如下:

65 typedef struct tagEDGE

66 {

67     double xi;

68     double dx;

69     int ymax;

74 }EDGE;

 根据EDGE的定义,扫描线4和扫描线7的“活动边表”就分别如图(7)和图(8)所示:

 

 图(7) 扫描线4的活动边表

 图(8) 扫描线7的活动边表

        前面提到过,扫描线算法的核心就是围绕“活动边表(AET)”展开的,为了方便活性边表的建立与更新,我们为每一条扫描线建立一个“新边表(NET)”,存放该扫描线第一次出现的边。当算法处理到某条扫描线时,就将这条扫描线的“新边表”中的所有边逐一插入到“活动边表”中。“新边表”通常在算法开始时建立,建立“新边表”的规则就是:如果某条边的较低端点(y坐标较小的那个点)的y坐标与扫描线y相等,则该边就是扫描线y的新边,应该加入扫描线y的“新边表”。上例中各扫描线的“新边表”如下图所示:

图(9) 各扫描线的新边表

        讨论完“活动边表(AET)”和“新边表(NET)”,就可以开始算法的具体实现了,但是在进一步详细介绍实现算法之前,还有以下几个关键的细节问题需要明确:

(1)      多边形顶点处理

        在对多边形的边进行求交的过程中,在两条边相连的顶点处会出现一些特殊情况,因为此时两条边会和扫描线各求的一个交点,也就是说,在顶点位置会出现两个交点。当出现这种情况的时候,会对填充产生影响,因为填充的过程是成对选择交点的过程,错误的计算交点个数,会造成填充异常。

        假设多边形按照顶点P1、P2和P3的顺序产生两条相邻的边,P2就是所说的顶点。多边形的顶点一般有四种情况,如图(10)所展示的那样,分别被称为左顶点、右顶点、上顶点和下顶点:

图(10) 多边形顶点的四种类型

左顶点――P1、P2和P3的y坐标满足条件 :y1 < y2 < y3;

右顶点――P1、P2和P3的y坐标满足条件 :y1 > y2 > y3;

上顶点――P1、P2和P3的y坐标满足条件 :y2 > y1 && y2 > y3;

下顶点――P1、P2和P3的y坐标满足条件 :y2 < y1 && y2 < y3;

        对于左顶点和右顶点的情况,如果不做特殊处理会导致奇偶奇数错误,常采用的修正方法是修改以顶点为终点的那条边的区间,将顶点排除在区间之外,也就是删除这条边的终点,这样在计算交点时,就可以少计算一个交点,平衡和交点奇偶个数。结合前文定义的“边”数据结构:EDGE,只要将该边的ymax修改为ymax – 1就可以了。

        对于上顶点和下顶点,一种处理方法是将交点计算做0个,也就是修正两条边的区间,将交点从两条边中排除;另一种处理方法是不做特殊处理,就计算2个交点,这样也能保证交点奇偶个数平衡。

(1)      水平边的处理

    水平边与扫描线重合,会产生很多交点,通常的做法是将水平边直接画出(填充),然后在后面的处理中就忽略水平边,不对其进行求交计算。

(2)      如何避免填充越过边界线

        边界像素的取舍问题也需要特别注意。多边形的边界与扫描线会产生两个交点,填充时如果对两个交点以及之间的区域都填充,容易造成填充范围扩大,影响最终光栅图形化显示的填充效果。为此,人们提出了“左闭右开”的原则,简单解释就是,如果扫描线交点是1和9,则实际填充的区间是[1,9),即不包括x坐标是9的那个点。

2.2扫描线算法实现

        扫描线算法的整个过程都是围绕“活动边表(AET)”展开的,为了正确初始化“活动边表”,需要初始化每条扫描线的“新边表(NET)”,首先定义“新边表”的数据结构。定义“新边表”为一个数组,数组的每个元素存放对应扫描线的所有“新边”。因此定义“新边表”如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/446947.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智能优化算法-蛇优化算法(SO)(附源码)

目录 1.内容介绍 2.部分代码 3.实验结果 4.内容获取 1.内容介绍 蛇优化算法 (Snake Optimization Algorithm, SO) 是一种基于群体智能的元启发式优化算法&#xff0c;它模拟了蛇的捕食行为、运动模式和社会互动&#xff0c;用于解决复杂的优化问题。 SO的工作机制主要包括&a…

二分类-多机器学习模型算法实现对比

准备数据 import pandas as pd import numpy as np import matplotlib.pyplot as plt from sklearn.preprocessing import StandardScaler from sklearn.preprocessing import OneHotEncoder from sklearn.preprocessing import PolynomialFeatures from sklearn.compose impo…

每日一刷——10.14——括号匹配(手写栈来实现)

栈与队列题目 第一题 题目 问题描述】设计一个算法判别一个算术表达式的圆括号是否正确配对 【输入形式】一个以为结尾的算术表达式 【输出形式】若配对&#xff0c;则输出圆括号的对数&#xff1b;否则输出no 【样例输入】 (ab)/(cd) 【样例输出】 2 【样例说明】共有两对括…

学习Redisson实现分布式锁

官网&#xff1a;https://redisson.org/ 官方文档&#xff1a;https://redisson.org/docs/getting-started/ 官方中文文档&#xff1a;https://github.com/redisson/redisson/wiki/%E7%9B%AE%E5%BD%95 1、引入依赖 <!--redisson--> <dependency><groupId>or…

【软件工程】数据流图DFD

文章目录 数据流图DFD概述一、数据流图的基本元素二、数据流图的绘制步骤三、数据流图的分层设计四、数据流图的绘制原则五、数据流图的应用 一个完整的数据流包含哪些要素从图中找出所有数据流1. **理解数据流图的结构**2. **识别外部实体**3. **追踪数据流**4. **记录数据流*…

SAP S/4 HANA 销售返利

目录 1 简介 2 后台配置 3 主数据 4 业务操作 4.1 场景 1 - 返利应计 4.2 场景 2 - 最终结算 1 简介 在过去 SAP ECC 把“返利”功能集成在了 SD 模块当中&#xff0c;而 SAP S/4 HANA 把“返利”集成在了结算管理功能模块当中。究其原因&#xff0c;主要是 ECC “返利”…

笔记-stm32移植ucos

文章目录 一、UCOS的基础知识1.1 前后台系统:1.2 RTOS系统可剥夺型内核:前后台系统和RTOS系统 1.3 UCOS系统简介学习方法 二、ucossii移植Step1&#xff1a;在工程中建立存放UCOSS代码的文件夹UCOSIIStep2:向CORE文件夹添加文件Step3:向Config文件夹添加文件Step4:向port文件夹…

本地拉取Docker镜像打包导入远程服务器

起因是因为使用远程服务器拉取镜像时&#xff0c;由于网络问题一直拉不成功&#xff0c;使用国内镜像由于更新不及时&#xff0c;国内镜像没有最新的 docker 镜像。最后使用本地的计算机&#xff0c;通过代理下载最新的镜像后打包成 tar&#xff0c; 然后上传到远程服务器进行导…

electron-vite打包踩坑记录

electron-vite打包踩坑记录 大前端已成趋势&#xff0c;用electron开发桌面端应用越来越普遍 近期尝试用electronvite开发了个桌面应用&#xff0c;electron-vite地址&#xff0c;可用使用vue开发&#xff0c;vite打包&#xff0c;这样就很方便了 但是&#xff0c;我尝试了一…

【机器学习】并行计算(parallel computation)Part1

为什么我们在机器学习中需要用到并行计算呢&#xff0c;因为现在最流行的机器学习算法都是神经网络&#xff0c;神经网络模型的计算量、参数量都很大&#xff0c;比如ResNet-50参数量为25M。而我们在训练的时候使用的数据集也很大&#xff0c;比如ImageNet数据集含有14M张图片。…

FileInputStream类

目录 1.案例代码&#xff1a; 2.注意细节 3.FileInputStream循环读取 1.案例代码&#xff1a; 准备的txt文件 结果&#xff1a; 如果需要输出原本的字母&#xff0c;强制转换为char即可&#xff1a; 结果&#xff1a; 2.注意细节 &#xff08;1&#xff09;如果文件不存在…

Qt和c++面试集合

目录 Qt面试 什么是信号&#xff08;Signal&#xff09;和槽&#xff08;Slot&#xff09;&#xff1f; 什么是Meta-Object系统&#xff1f; 什么是Qt的MVC模式&#xff1f; 1. QT中connect函数的第五个参数是什么&#xff1f;有什么作用&#xff1f; 3. 在QT中&#xff…

【NestJS入门到精通】装饰器

目录 方法装饰器通过prototype添加属性、方法 属性装饰器拓展 方法装饰器参数装饰器 方法装饰器 ClassDecorator 定义了一个类装饰器 a&#xff0c;并将其应用于类 A。装饰器 a 会在类 A 被定义时执行。 const a:ClassDecorator (target:any)>{console.log(target,targe…

概率 多维随机变量与分布

一、二维 1、二维随机变量及其分布 假设E是随机试验&#xff0c;Ω是样本空间&#xff0c;X、Y是Ω的两个变量&#xff1b;(X,Y)就叫做二维随机变量或二维随机向量。X、Y来自同一个样本空间。 联合分布函数 F(x,y)P(X≤x,Y≤y)&#xff0c;即F(x,y)表示求(x,y)左下方的面积。 …

Struct Streaming

spark进行实时数据流计算时有两个工具 Spark Streaming:编写rdd代码处理数据流,可以解决非结构化的流式数据 Structured Streaming:编写df代码处理数据流,可以解决结构化和半结构化的流式数据 实时计算 实时计算&#xff0c;通常也称为“实时流计算”、“流式计算” 流数据处…

Unity3d使用JsonUtility.FromJson读取json文件

使用JsonUtility.FromJson方法不需要额外引用第三方库。该方法只能读取json对象&#xff0c;而不能读取json数组。 假如我们有如下的json数组&#xff1a; [ {"id":1, "name":"first2021", "level":5, "score":100, "…

vue3 对 vue2 有什么优势

1、diff算法的优化--静态标记&#xff08;PatchFlag&#xff09; vue2中的虚拟dom是全量的对比&#xff08;每个节点不论写死的还是动态的都会一层一层比较&#xff0c;这就浪费了大部分事件在对比静态节点上&#xff09; vue3编译模板时&#xff0c;动态节点做标记 标记分为不…

仿函数(函数对象)

0.含义 仿函数和函数对象在C中含义一致。官方解释是&#xff1a; &#xff08;&#xff09;就是函数调用运算符&#xff0c;也就是说一个类重载了小括号&#xff0c;它实例化的对象就可以像函数一样使用。 “仿”函数&#xff0c;意味着它和函数使用有相同点&#xff1a; …

盘点双十一四款不错的品牌好物!2024学生党高颜值平价好物推荐!

在双十一这个购物狂欢节&#xff0c;不少学生党都希望以最实惠的价格买到心仪的商品。今天&#xff0c;我们就来盘点四款双十一期间值得入手的高颜值平价好物&#xff0c;让同学们在享受优惠的同时&#xff0c;也能拥有品质生活&#xff01; 品牌好物一、希亦CG超声波清洗机 双…

数据中心物理安全的历史和演变

在当今的数字时代&#xff0c;数据中心托管已成为我们互联世界的支柱。这些设施在存储、管理和处理我们日常生活所需的大量信息方面发挥着至关重要的作用。从社交媒体平台和电子商务网站到流媒体服务和云计算&#xff0c;数据中心为我们依赖的数字服务提供支持。 随着企业越来…