计算机图形学:直线的扫描转换算法解析与实现

直线的扫描转换:

DDA算法:

推理:

在计算机显示图形时,由于显示计算机的分辨率是有限的所以我们在绘制图形时需要将图形从连续量转换成离散量才能完成图形的绘制,直线的扫描转换就是将连续量转换为离散量的过程。
对于任意给我们两点 ( x 0 , y 0 ) (x_0, y_0) (x0,y0) ( x 1 , y 1 ) (x_1, y_1) (x1,y1)我们可以得到小面的一些量:
k = y 1 − y 0 x 1 − x 0 k = \frac{y_1-y_0}{x_1-x_0} k=x1x0y1y0
此值的意义表示的是在x轴上自增1,在y轴上的增量。如图所示:
在这里插入图片描述

这样我们得到斜率之后,我们就知道在x轴上增加1,y轴上的增量了,但是目前我们仍旧不知道该怎么将这个这两点所确定的直线转换为离散的点集。
我们将这个图形转化为下图看一下:
在这里插入图片描述
在这里插入图片描述

也就是说对于直线上一点我们可以使用最接近这一点的像素来表示这一点。
所以我们就可以就可以将直线转换为一个一个离散的点了,为了获取最多的能够表示这一条直线的点,对于斜率为k的直线,我们需要沿着增量最快的轴作为自变量来增加,因为像素是一个挨着一个,所以作为自变量的轴每一次增加1,因为k的取值可以变化,无论k值怎样变化,第一次决策的点都应该属于下面的八个点,这样我们么一次抉择下一个点就是在周围的八个点之间做一个决策:
在这里插入图片描述
在这里插入图片描述

当k选择一个具体的值之后如k=0.5则直线的下一次取点的决策范围应该为:
在这里插入图片描述

现在我们已经说明了为什么要选择增量最快的轴作为自变量轴,同时也说明了为什么作为自变量的轴每一次要增加1而不是增加2或者其他的数字。
如果不使用增量最快的轴作为自变量轴则会出现什么情况能。如起始点(2,2),斜率为k的直线选择变化慢的轴作为自变量,则决策范围为:
在这里插入图片描述

可以看出这种取法会导致我们选取的点不是最符合直线的点。
所以我们要采取自变量变化快的轴作为我们的自变量,每次变化1.
如果根据起点与终点确定了点的坐标,我们同样要根据点的坐标进行舍入,来确定整数坐标的像素作为表是直线的点,如何进行舍入呢,我们以(2,2)点表示的像素为例,我们只需要确定(x,y)是不是属于红色区域的点
在这里插入图片描述

公式为:
1.5 < = x < 2.5 → x = 2 1.5 < = y < 2.5 → y = 2 1.5<=x<2.5\rightarrow x = 2\\ 1.5<=y<2.5\rightarrow y = 2 1.5<=x<2.5x=21.5<=y<2.5y=2
因为c语言中的取整规则是向下取整,所以我们在确定
点的时候要将x,y分别加上0.5,用(x+0.5, y+0.5)取整来确定像素点。

公式推导

起点 ( x 起 , y 起 ) 终点 ( x 终 , y 终 ) k = y 终 − y 起 x 终 − x 起 Δ x = x 终 − x 起 Δ y = y 终 − y 起 k = Δ y Δ x 如果 0 ≤ k ≤ 1 则说明 x 轴变化快,选择 x 作为自变量 起始点 ( x 起 , y 起 ) : x 1 = x 起 + 1 y 1 = y 起 + k 选取点的时候选取: ( ⌊ x 1 + 0.5 ⌋ , ⌊ y 1 + 0.5 ⌋ ) x 2 = x 1 + 1 y 2 = y 1 + k 选取点的时候选取: ( ⌊ x 2 + 0.5 ⌋ , ⌊ y 2 + 0.5 ⌋ ) . . . . . . 起点(x_起, y_起)终点(x_终, y_终)\\ k = \frac{y_终-y_起}{x_终-x_起}\\ \Delta x = x_终- x_起\\ \Delta y = y_终-y_起\\ k = \frac{\Delta y}{\Delta x}\\ 如果0\leq k\leq1则说明x轴变化快,选择x作为自变量\\ 起始点(x_起, y_起):\\ x_1 = x_起 + 1\\ y_1 = y_起 + k\\ 选取点的时候选取:(\lfloor x_1+0.5\rfloor, \lfloor y_1+0.5\rfloor)\\ x_2 = x_1 + 1\\ y_2 = y_1 + k\\ 选取点的时候选取:(\lfloor x_2+0.5\rfloor, \lfloor y_2+0.5\rfloor)\\ ...... 起点(x,y)终点(x,y)k=xxyyΔx=xxΔy=yyk=ΔxΔy如果0k1则说明x轴变化快,选择x作为自变量起始点(x,y):x1=x+1y1=y+k选取点的时候选取:(⌊x1+0.5,y1+0.5⌋)x2=x1+1y2=y1+k选取点的时候选取:(⌊x2+0.5,y2+0.5⌋)......
所以我们先计算k,然后一步一步的进行计算即可。
但是上述只讨论了 0 < = k < = 1 0<=k<=1 0<=k<=1这一种情况,但是这已经够用了,因为对于坐标系内任意一点都可以通过一些对称轴转化至 0 < = k < = 1 0<=k<=1 0<=k<=1区域内。
对于 k > 1 k>1 k>1的情况,我们知道此时变化较快的轴为y轴所以选取y轴为增加1的轴,其余分析同上。
如图:
在这里插入图片描述

我们可以通过对称轴y=x将上方区域的点转换到下方区域。其他区域也是同理。将点进行对称时一定要按照以起点为原点进行对称,也就是说这涉及到一个坐标系变换的问题,我们根据坐标系的变换规则我们可以知道
(1)当要进行终点在起始点的左上方时:
在这里插入图片描述

$x = x1+2*(x0-x1) =2x0-x1\
y = y1
$
其余同理可得:
(2)当终点在起始点的左下方的时候:
x = x 1 + 2 ∗ ( x 0 − x 1 ) = 2 ∗ x 0 − x 1 y = y 1 + 2 ∗ ( y 0 − y 1 ) = 2 ∗ y 0 − x 1 x = x1+2*(x0-x1) =2*x0-x1\\ y = y1 + 2*(y0-y1) = 2*y0-x1 x=x1+2(x0x1)=2x0x1y=y1+2(y0y1)=2y0x1
(3)当终点在起始点的右下方的时候:
$
x =x1\
y = y1 + 2
(y0-y1) = 2*y0-x1
$
然后通过对称过去的点计算出点的坐标然后对称回来进行绘制即可。
我们可以写出下述算法:

void paintline(CPoint start, CPoint end, CDC* pDC, int deltax, int deltay, int flag ) {int num;float x1, y1, xIncreation, yIncreation;CPoint point;CPoint end2;switch (flag) {case 1:end2.x = end.x;end2.y = end.y;break;case 2:end2.x = 2 * start.x - end.x;end2.y = end.y;break;case 3:end2.x = 2 * start.x - end.x;end2.y = 2 * start.y - end.y;break;case 4:end2.y = 2 * start.y - end.y;end2.x = end.x;break;}deltax = end2.x - start.x;deltay = end2.y - start.y;num = deltax > deltay ? deltax : deltay;if (deltax > deltay) {yIncreation = 1.0 * deltay / deltax;xIncreation = 1;}else {xIncreation = 1.0 * deltax / deltay;yIncreation = 1;}x1 = start.x;y1 = start.y;for (int i = 0; i < num; i++) {point.x = (int)(x1 + 0.5);point.y = (int)(y1 + 0.5);switch (flag) {case 1:pDC->SetPixelV(point, RGB(255, 0, 0));break;case 2:point.x = 2 * start.x - point.x;pDC->SetPixelV(point, RGB(255, 0, 0));break;case 3:point.x = 2 * start.x - point.x;point.y = 2 * start.y - point.y;pDC->SetPixelV(point, RGB(255, 0, 0));break;case 4:point.y = 2 * start.y - point.y;pDC->SetPixelV(point, RGB(255, 0, 0));break;}x1 += xIncreation;y1 += yIncreation;}
}
void paintLine(CPoint start, CPoint end, CDC * pDC) {int deltax = end.x - start.x;int deltay = end.y - start.y;if (deltax > 0 && deltay > 0) {paintline(start, end, pDC, deltax, deltay, 1);}else if (deltax < 0 && deltay>0) {paintline(start, end, pDC, deltax, deltay, 2);}else if (deltax < 0 && deltay < 0) {paintline(start, end, pDC, deltax, deltay, 3);}else if (deltax > 0 && deltay < 0) {paintline(start, end, pDC, deltax, deltay, 4);}}void Ctest2Dlg::OnLButtonDown(UINT nFlags, CPoint point)
{if (!flag) {start = point;flag = !flag;}else {end = point;flag = !flag;CDC* pDC = GetDC();paintLine(start, end, pDC);ReleaseDC(pDC);}CDialogEx::OnLButtonDown(nFlags, point);
}

中点画线法

算法原理

与DDA算法不同中点画线法的主要思想是这样的,对于任意一条直线来说这条直线会将平面分成上下两部分,将直线上方的点代入到直线方程中会得到大于0的结果,将直线下方的点代入直线方程中会得到小于0的结果。
在这里插入图片描述
也就是说如果我们代入两个像素的中间值,会得到大于0或小于0的结果,当大于0时我们需要选取直线下方的点作为我们的选取的点,当代入小于0的时候我们需要选取上方的点作为我们选取的点。
我们先推导0<=k<=1时选取点的条件:
设方程为 y = k ∗ x + b y = k*x+b y=kx+b假设现在选取的点为 ( x , y ) (x, y) (x,y)则下一点需要在 ( x + 1 , y + 0 ) (x+1, y+0) (x+1,y+0) ( x + 1 , y + 1 ) (x+1, y+1) (x+1,y+1)做出选择,此时我们将 ( x + 1 , y + 0.5 ) (x+1, y+0.5) (x+1,y+0.5)代入得到 d = y + 0.5 − k ∗ ( x + 1 ) − b d = y+0.5-k*(x+1)-b d=y+0.5k(x+1)b此时d>0 或 d<0

  1. 当d>0时选择(x+1, y)点
    下面那一点就从 ( x + 2 , y + 0 ) (x+2, y+0) (x+2,y+0) ( x + 2 , y + 1 ) (x+2, y+1) (x+2,y+1)之中选择,将 ( x + 2 , y + 0.5 ) (x+2, y+0.5) (x+2,y+0.5)代入,$\d_{next} = y+0.5 - k*(x+1)-b\$ d = y + 0.5 − k ∗ ( x + 2 ) − b d = y+0.5-k*(x+2)-b d=y+0.5k(x+2)b
    d n e x t = d + 0.5 − k d_{next} = d+0.5-k dnext=d+0.5k
    d n e x t − d = − k d_{next} - d = -k dnextd=k
  2. 当d<0时选择(x+1, y+1)点.
    下面那一点就从 ( x + 2 , y + 1 ) (x+2, y+1) (x+2,y+1) ( x + 2 , y + 2 ) (x+2, y+2) (x+2,y+2)之中选择,将 ( x + 2 , y + 1.5 ) (x+2, y+1.5) (x+2,y+1.5)代入,$\d_{next} = y+1.5 - k*(x+1)-b\$ d = y + 0.5 − k ∗ ( x + 2 ) − b d = y+0.5-k*(x+2)-b d=y+0.5k(x+2)b
    d n e x t = d + 1 − k d_{next} = d+1-k dnext=d+1k
    d n e x t − d = 1 − k d_{next} - d = 1-k dnextd=1k
    我们知道了增量是如何变化的还不够,我们需要求出初始d的值,
    d = y − k ∗ x + b d = y-k*x+b d=ykx+b,因为初始点 ( x 0 , y 0 ) (x_0, y_0) (x0,y0)在直线上将点(x_0+1, y_0+0.5)代入即可求出初始d。
    d = 0.5 + k;
    因为k>1的情况与0<=k<=1可以通过对称得到,所以对于k>1的情况我们将所有的x换成y,所有的y换成x就能得到k>1情况下的中点画线法。

减少浮点运算

因为增量与-k或0.5-k有关,这种是可能涉及到浮点运算的,为了避免浮点运算,我们通过*2 Δ x \Delta x Δx来将浮点运算转换为整数运算。
此时对于所有过程我们都要乘上2 Δ x \Delta x Δx
由此得到初始 d = Δ x − 2 ∗ Δ y d = \Delta x - 2*\Delta y d=Δx2Δy
相应的d的增量变成 − 2 ∗ Δ y -2*\Delta y 2Δy 2 ∗ Δ x − 2 ∗ Δ y 2*\Delta x - 2*\Delta y 2Δx2Δy

算法实现

算法的实现与上面DDA算法类似。
代码如下:

void paintline(CPoint start, CPoint end, CDC* pDC, int deltax, int deltay, int flag) {int num;int d;CPoint point, point2;CPoint end2;switch (flag) {case 1:end2.x = end.x;end2.y = end.y;break;case 2:end2.x = 2 * start.x - end.x;end2.y = end.y;break;case 3:end2.x = 2 * start.x - end.x;end2.y = 2 * start.y - end.y;break;case 4:end2.y = 2 * start.y - end.y;end2.x = end.x;break;}deltax = end2.x - start.x;deltay = end2.y - start.y;num = deltax > deltay ? deltax : deltay;if (deltax >= deltay) {d = deltax - 2 * deltay;}else {d = deltay - 2 * deltax;}point.x = start.x;point.y = start.y;for (int i = 0; i < num; i++) {switch (flag) {case 1:point2.x = point.x;point2.y = point.y;pDC->SetPixelV(point2, RGB(255, 0, 0));break;case 2:point2.x = 2 * start.x - point.x;point2.y = point.y;pDC->SetPixelV(point2, RGB(255, 0, 0));break;case 3:point2.x = 2 * start.x - point.x;point2.y = 2 * start.y - point.y;pDC->SetPixelV(point2, RGB(255, 0, 0));break;case 4:point2.y = 2 * start.y - point.y;point2.x = point.x;pDC->SetPixelV(point2, RGB(255, 0, 0));break;}if (d > 0) {if (deltax >= deltay) {point.x += 1;d = d - 2 * deltay;}else {point.y += 1;d = d - 2 * deltax;}}else {point.x += 1;point.y += 1;if (deltax >= deltay) {d = d + (2 * deltax - 2 * deltay);}else {	d = d + (2 * deltay - 2 * deltax);}}}
}
void paintLine(CPoint start, CPoint end, CDC* pDC) {int deltax = end.x - start.x;int deltay = end.y - start.y;if (deltax > 0 && deltay > 0) {paintline(start, end, pDC, deltax, deltay, 1);}else if (deltax < 0 && deltay>0) {paintline(start, end, pDC, deltax, deltay, 2);}else if (deltax < 0 && deltay < 0) {paintline(start, end, pDC, deltax, deltay, 3);}else if (deltax > 0 && deltay < 0) {paintline(start, end, pDC, deltax, deltay, 4);}}

这些代码都可以进行一下优化,这样能够提高效率,大家可以自行去优化。

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

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

相关文章

wsj0数据集原始文件.wv1.wv2转换成wav文件

文章目录 准备一、获取WSJO数据集二、安装sph2pipe三、转换代码四、结果展示 ​ 最近做语音分离实验需要用到wsj0-2mix数据集&#xff0c;但是从李宏毅语音分离教程里面获取的wsj0-2mix只有一部分。从网上获取到了完整的WSJO数据集后&#xff0c;由于原始的语音文件后缀是wv1或…

怎么在NAS里找照片?教你一招,精准定位

每次拍照 咔咔一顿拍 好多文档 咔咔一顿存 需要到的时候 却依稀只记得时间和部分关键词 那么怎么快速在NAS里精准定位 找到“命中注定”的它呢 嘿还真有 铁威马的Terra Search 精准搜索 快速定位 So easy&#xff01; 01 什么是Terra Search Terra Search 通过建立数据…

中国信通院发布《中国算力发展指数白皮书》(2023)

加gzh“大数据食铁兽”&#xff0c;回复“20231129”&#xff0c;获取材料完整版 导读 2023 年白皮书在 2022 年的基础上&#xff0c;加强了全球和我国算力发展的研究&#xff0c;客观评估我国整体、各省份及各城市现阶段的算力发展水平进一步给出我国算力二十强市榜单&…

网关路由器双栈配置中的IPv6相关选项解析

1、引言 讲知识往往是枯燥无味的&#xff0c;我们先从问题入手。家庭网关&#xff08;光猫&#xff09;、路由器是我们每个人或多或少都有所接触的2种设备。现在一般都是光纤入户&#xff0c;通常每个家庭配备一个光猫和一台家用路由器。 目前有许多网络服务已经提供了IPv6支…

ASUS(华硕) B760M-AYW WIFI D4_解决wifi不能使用

1、最近新购买了一套 diy电脑主机&#xff0c;选用的是 ASUS B760M-AYW WIFI D4电脑主板 win10 系统&#xff0c;到货后 发现右下角电脑图标处及网络适配器中 没有wifi选项 首先 在官网和旗舰店客服处&#xff0c;确认了 该主板 有集成wifi模块&#xff0c;鲨鱼鳍天线未安装…

Motion Plan之带动力学约束路径搜索

Motion Plan之搜索算法笔记 Motion Plan之基于采样的路径规划算法笔记 为什么要动力学规划&#xff1a; 前面几章介绍的路径规划&#xff0c;我们只是认为机器人是质点&#xff0c;这节课要说的就是&#xff0c;如何在考虑机器人的运动学模型下再去找一个安全可行的路径。考虑…

后端项目连接数据库-添加MyBatis依赖并检测是否成功

一.在pom.xml添加Mybatis相关依赖 在Spring Boot项目中&#xff0c;编译时会自动加载项目依赖&#xff0c;然后使用依赖包。 需要在根目录下pom.xml文件中添加Mybatis依赖项 <!-- Mybatis整合Spring Boot的依赖项 --> <dependency><groupId>org.mybatis.s…

UE Web Remote Control

前言 最近在研究UE自启WEB服务和网页通信以此来通过网页与UE进行数据交互&#xff0c;这样最好的方式就是可以摒弃掉整个繁琐的通信连接流程如TCP UDP&#xff0c;但是找到的一些方法都不是很适用&#xff0c;尤其是WEBUI这个插件它只适合内嵌到UE本身才能完成交互&#xff0c;…

LeetCode(37)矩阵置零【矩阵】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 73. 矩阵置零 1.题目 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]…

仓储货架生产厂家|拥有编码器+激光传感器的海格里斯HEGERLS料箱式四向穿梭车

随着高新科技的迅猛发展&#xff0c;仓储物流行业已慢慢朝着无人化、自动化、智能化、密集化方向快速发展&#xff0c;用户的需求量也随之日益提升。在众多仓储物流设备中&#xff0c;四向穿梭车越来越得到各大中小企业所青睐和投入使用。四向穿梭车不但具有良好的可延性与适配…

2022年土地出让数据,超多字段,附数据可视化

分享一个土地出让数据&#xff0c;详细信息如下&#xff1a; 数据名称: 2022年土地出让数据 数据格式: Shp、excel 数据时间: 2022年 数据几何类型: 点 数据坐标系: WGS84坐标系 数据来源&#xff1a;网络公开数据 部分字段如下&#xff1a; 如需获取可搜“吧唧数…

【面试心经】——上海广升智能科技有限公司

介绍 本文记录的是2018.5月份应聘上海广升linux 开发岗位的经历。 福利待遇&#xff1a; BOSS 岗位薪资&#xff1a;15K ~25K。 每月一天带薪病假或事假。 国家规定外有额外4天年假。 每年两次旅游&#xff0c;上半年国外&#xff0c;下半年国内。 每年两次调薪机会&…

Ultipa参加国际科学会议KGSWC2023

近日&#xff0c;领先的国际科学会议 KGSWC 2023&#xff0c;在西班牙萨拉戈萨大学召开&#xff0c;Ultipa Graph参加。 KGSWC2023是第五届伊比利亚-美洲会议和第四届印度-美洲知识图谱与语义网大会的联合论坛。自2019年成立以来&#xff0c;KGSWC一直是一个重要的学术活动&am…

测试工程师核心软技能「情绪管理」

大家好呀&#xff0c;我是楼仔。 我之前经常提到一句话&#xff1a;大多数时候所谓的“技术之玻璃天花板”&#xff0c;其实只是缺乏软技能而已。 所以粉丝朋友们&#xff0c;我们除了需要关注技术&#xff0c;更需要注重软技能的提高。 关于软技能相关的文章&#xff0c;之…

婚恋小程序APP一站式开发搭建丨幸福全方位婚恋一线牵丨相亲交友婚恋小程序使用手册丨实名认证丨语音交友丨源码交付,支持二开丨同城交友

1: 如何在相亲交友婚恋小程序寻找属于自己的幸福 打开相亲交友婚恋小程序&#xff0c; 在首页遇到自己喜欢的人&#xff0c; 点击详情&#xff0c; 然后点击喜欢&#xff0c; 如果对方也喜欢你&#xff0c; 互相喜欢成功&#xff0c; 可以在缘分页看到对方的微信&#xff0c; 添…

基于YOLOv8深度学习的PCB板缺陷检测系统【python源码+Pyqt5界面+数据集+训练代码】目标检测

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能AI、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推荐--…

Android Studio Giraffe-2022.3.1-Patch-3安装注意事项

准备工作&#xff1a; android studio下载地址&#xff1a;https://developer.android.google.cn/studio/releases?hlzh-cn gradle下载地址&#xff1a;https://services.gradle.org/distributions/ 比较稳定的网络环境&#xff08;比较android studio相关的依赖需要从谷歌那边…

Redis集群模式

Redis集群主要有三种模式 主从复制模式&#xff08;Master-Slave&#xff09;、哨兵模式&#xff08;Sentinel&#xff09;和Cluster模式 主从复制模式&#xff1a;适用于数据备份和读写分离场景&#xff0c;配置简单&#xff0c;但在主节点故障时需要手动切换。哨兵模式&…

Google分析中的基础概念

当提到Google分析时&#xff0c;我们通常指的是一种用于跟踪和分析网站和应用程序数据的工具。在使用Google分析之前&#xff0c;了解其基础概念对于正确配置和有效使用该工具非常重要。 1、帐户&#xff08;Account&#xff09;&#xff1a;帐户是Google分析中的最高层级。一…

【网络安全】-安全常见术语介绍

文章目录 介绍1. 防火墙&#xff08;Firewall&#xff09;定义通俗解释 2. 恶意软件&#xff08;Malware&#xff09;定义通俗解释 3. 加密&#xff08;Encryption&#xff09;定义通俗解释 4. 多因素认证&#xff08;Multi-Factor Authentication&#xff0c;MFA&#xff09;定…