我是用MFC框架进行测试的,由于本人也没有专门系统学习MFC框架,代码若有不足之处,请指出。

一,先来一个最简单的DDA算法

DDA算法全称为数值微分法,基于微分方程来绘制直线。

①推导微分方程如下:

②,dM时间步长的倒数的详解:

可以看到

当|k|<=1时

dx=1或者-1,此时的x为计长方向

当|k|>1时

dy=1或者-1,此时的y为计长方向

绘制时需要用dM来控制绘制的点数

③绘制像素的问题:

为了“方便”管理算法,我为不同的绘制函数新建了一个类了。。。(其实可以写到一个类里面。。。。)

④代码实现:

MyDDA.cpp

 #include "stdafx.h"
#include "MyDDA.h" MyDDA::MyDDA()
{
} MyDDA::~MyDDA()
{
} void MyDDA::SetDc(CDC * dc)
{
this->pdc = dc;
} void MyDDA::Moveline(CPoint start, CPoint end)
{
int x1, x2;
int y1, y2;
x1 = start.x;
x2 = end.x;
y1 = start.y;
y2 = end.y;
float dm = ;
if (abs(x2 - x1) >= abs(y2 - y1))
dm = abs(x2 - x1);
else
dm = abs(y2 - y1);
float dx = (float)(x2 - x1) / dm;
float dy = (float)(y2 - y1) / dm;
float x = x1 + 0.5;
float y = y1 + 0.5;
int i = ;
while (i<dm) {
this->pdc->SetPixel((int)x, (int)y, RGB(, , ));
x += dx;
y += dy;
i += ;
} }

总结:

其实这个算法还算是挺简单的,就是要确定是X还算Y为计长方向,需要注意的是循环过程中计长方向自增1,另一个方向按照当直线在计长方向自增1时,直线在轴上的投影的大小自增就可以了。

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

二,逐点比较法

逐点比较法是在画线的过程中,每画一点就与理想直线比较,以决定下一点的走向,以一步步逼近的方法点亮最接近直线的一组像素。

 ①绘制前先将直线平移

使y坐标较小的点位于坐标原点(绘制的时候再平移回去,跟几何变换一个道理)

如图:

②推导过程:

先来谈一下直线的偏移值怎么表示

可以看到:

如果d是正值说明当前绘制点在绘制直线的上方。

反之,在下方。

③由于每次计算都需要计算两次乘法,计算工作量大,所以可以利用递推公式来,借助当前点的偏移值,获取下一个点的偏移值。

递推公式推导过程如下图所示:

④终点判断:

 ⑤整理下我们在编程时需要的参数:

⑥实现代码:

注释部分的代码比较容易理解一点,但是太长了,注释下面的代码是简化的版本,两者的作用是一样的。

PtoP.cpp

 void PtoP::Moveline(CPoint start, CPoint end)
{
int xA, yA;
if (start.y > end.y) {
xA = start.x - end.x;
yA = start.y - end.y;
}
else
{
xA = end.x - start.x;
yA = end.y - start.y;
} int n = abs(xA) + abs(yA); int x = , y = , F = ;
/*
if (xA > 0)//1
{
for (int i = 0; i < n; i++)
{
if (F >= 0)
{
x += 1;
F -= yA;
if(start.y>end.y)
this->pdc->SetPixel(x+end.x,y+end.y, RGB(0, 0, 0));
else
this->pdc->SetPixel(x + start.x, y + start.y, RGB(0, 0, 0));
}
else
{
y += 1;
F += xA;
if (start.y > end.y)
this->pdc->SetPixel(x + end.x, y + end.y, RGB(0, 0, 0));
else
this->pdc->SetPixel(x + start.x, y + start.y, RGB(0, 0, 0));
}
} }
else//2
{
for (int i = 0; i < n; i++)
{
if (F >= 0)
{
y += 1;
F += xA;
if (start.y > end.y)
this->pdc->SetPixel(x + end.x, y + end.y, RGB(0, 0, 0));
else
this->pdc->SetPixel(x + start.x, y + start.y, RGB(0, 0, 0));
}
else
{
x-= 1;
F += yA;
if (start.y > end.y)
this->pdc->SetPixel(x + end.x, y + end.y, RGB(0, 0, 0));
else
this->pdc->SetPixel(x + start.x, y + start.y, RGB(0, 0, 0));
} } }
*/
for (int i = ; i < n; i++) {
if (xA > ) {
if (F >= )
{
x++;
F -= yA;
}
else
{
y++;
F += xA;
}
}
else {
if (F >= )
{
y++;
F += xA;
}
else
{
x--;
F += yA;
}
}
if (start.y > end.y)
this->pdc->SetPixel(x + end.x, y + end.y, RGB(, , ));
else
this->pdc->SetPixel(x + start.x, y + start.y, RGB(, , ));
}
}

⑦总结:

逐点比较法绘制最重要的是递推公式的推导,以及避免原偏移值公式的无理运算。

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

三,Bresenham画线法

①引入

Bresenham画线算法是应用最广泛的直线生成算法,它采用加减以及乘2运算(即移位运算)来实现。

首先先从直线斜率为0<=k<=1的八分之一象限来开始讨论。

如图:

 ②如何判别选取点S还是点T

③迭代公式

④起点的判别值

 ⑤其他情况的判别

⑥整理所需变量

⑦代码实现

Bresenham.cpp

 #include "stdafx.h"
#include "Bresenham.h" Bresenham::Bresenham()
{
} Bresenham::~Bresenham()
{
} void Bresenham::SetDc(CDC * dc)
{
this->pdc = dc;
} void Bresenham::Moveline(CPoint start, CPoint end)
{
int x1=start.x, y1=start.y;
int x2=end.x, y2=end.y; this->pdc->SetPixel(start.x,start.y,RGB(,,));
int dx, dy;
dx = abs(x2-x1);
dy = abs(y2-y1);
int flag=;
if (dx == && dy == )
return;
if (dy > dx)
{
flag = ;
swap_value(x1,y1);
swap_value(x2,y2);
swap_value(dx,dy);
}
int tx = (x2 - x1) > ? : -;
int ty = (y2 - y1) > ? : -;
int curx = x1 + ;
int cury = y1;
int dS = * dy;
int dT = * (dy-dx);
int d =dS-dx;
while (curx != x2)
{
if (d >= ) {
d += dT;
cury += ty;
}
else
{
d += dS;
}
if (flag)
this->pdc->SetPixel(cury,curx,RGB(,,));
else
this->pdc->SetPixel(curx, cury, RGB(, , ));
curx+=tx;
}
} void Bresenham::swap_value(int & a, int & b)
{
/*
a ^= b;
b ^= a;
a ^= b;
*/
int temp = a;
a = b;
b = temp; }

总结:Bresenham算法关键点还是在于迭代公式的推导,以及如何选择下一个点,判断x轴和y轴的步长是自增还是自减

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

四,中点画线法

中点画线法的思路跟Bresenham算法的思路极为相似,在学习完之后,我就觉得两者的算法不同之处就是取点的判断标准

①构造判别式

②递推公式推导

③其他斜率情形

 ④整理变量

⑤代码实现

 #include "stdafx.h"
#include "MidpointLine.h" MidpointLine::MidpointLine()
{
} MidpointLine::~MidpointLine()
{
} void MidpointLine::SetDc(CDC * dc)
{
this->pdc = dc;
} void MidpointLine::Moveline(CPoint start, CPoint end)
{
int x0 = start.x, x1 = end.x, y0 = start.y, y1 = end.y;
int a, b, d2, x, y, flag = ;
if (abs(x1 - x0) < abs(y1 - y0))
{
swap_value(x0,y0);
swap_value(x1, y1);
flag = ;
}
if (x0 > x1) {//保证x0<x1,方便判别斜率
swap_value(x0, x1);
swap_value(y0, y1);
}
a = y0 - y1;
b = x1 - x0;
d2 = *a + b;//摆脱小数点,提高效率
if (y0 < y1) {//k>0
x = x0; y = y0;
this->pdc->SetPixel(x,y,RGB(,,));
while (x < x1)
{
if (d2 < )
{
x++;
y++;
d2 =d2+ *a + *b;
}
else {
x++;
d2 =d2+ * a;
} if(flag)//|k|>1
this->pdc->SetPixel(y, x, RGB(, , ));
else
this->pdc->SetPixel(x, y, RGB(, , ));
}
}
else {//k<0
x = x1;
y = y1;
this->pdc->SetPixel(x, y, RGB(, , ));
while (x >x0)
{
if (d2 < )
{
x--;
y++;
d2 = d2- * a + * b;
}
else {
x--;
d2 =d2- * a;
} if (flag)//|k|>1
this->pdc->SetPixel(y, x, RGB(, , ));
else
this->pdc->SetPixel(x, y, RGB(, , ));
}
}
} void MidpointLine::swap_value(int & a, int & b)
{
/*
a ^= b;
b ^= a;
a ^= b;
*/
int temp = a;
a = b;
b = temp;
}

总结:

判断的准则不同也导致函数的写法跟之前Bresenham函数有很大的差别,最明显的就是中点判断法需要保证绘制直线的走向,以方便判断直线的斜率,而Bresenham算法则不需要这一点,它只需要知道tx,ty就能知道直线的走向,对于起点,终点在哪里。

并没有太大的约束。

最后来一张四种算法同时绘制直线的合照吧。。。。

最左边是DDA,依次是逐点比较法,Bresenham,中点画线法。

该工程的github链接:https://github.com/Thousandyearsofwar/DrawLine

【OpenGL学习】 四种绘制直线的算法的更多相关文章

  1. Java实现操作系统中四种动态内存分配算法:BF+NF+WF+FF

    1 概述 本文是利用Java实现操作系统中的四种动态内存分配方式 ,分别是: BF NF WF FF 分两部分,第一部分是介绍四种分配方式的概念以及例子,第二部分是代码实现以及讲解. 2 四种分配方式 ...

  2. Dubbo -- 四种loadBalance负载均衡算法

    Dubbo中的一个关键接口LoadBalance,dubbo是按照其中的规则来调用多台provider的服务的. 先看一下接口的实现类图: 从上图中我们可以看到dubbo提供了四种算法来实现负载均衡. ...

  3. 用php实现四种常见的排序算法

    几种常见的排序 排序是一个程序员的基本功,对于初级phper,更是可以通过排序算法来锻炼自己的思维能力. 所谓排序,就是对一组数据,按照某个顺序排列的过程.下面就总结四种常用的php排序算法,分别是冒 ...

  4. 四 akka学习 四种多线程的解决方案

    http://blog.csdn.net/chenleixing/article/details/44044243 四种多线程的解决方案

  5. 四种简单的排序算法的php实现

    无聊,用php写几个排序算法,算法介绍请移步这里,这里使用php实现了几个简单的,如下 //选择排序 function selection_sort($arr){ $len = count($arr) ...

  6. ADO.NET基础学习-----四种模型,防止SQL注入

    1.ExcuteNonQuery 执行非查询语句,返回受影响的行数. // 1.ExcuteNonQuery string sqlconn = "Data Source=wss;Initia ...

  7. 用MATLAB结合四种方法搜寻罗马尼亚度假问题

    选修了cs的AI课,开始有点不适应,只能用matlab硬着头皮上了,不过matlab代码全网仅此一份,倒有点小自豪. 一.练习题目 分别用宽度优先.深度优先.贪婪算法和 A*算法求解"罗马利 ...

  8. golang实现四种排序(快速,冒泡,插入,选择)

    本文系转载 原文地址: http://www.limerence2017.com/2019/06/29/golang07/ 前面已经介绍golang基本的语法和容器了,这一篇文章用golang实现四种 ...

  9. OpenGL学习进程(10)第七课:四边形绘制与动画基础

        本节是OpenGL学习的第七个课时,下面以四边形为例介绍绘制OpenGL动画的相关知识:     (1)绘制几种不同的四边形: 1)四边形(GL_QUADS) OpenGL的GL_QUADS图 ...

随机推荐

  1. 002_软件安装之_keil4与keil5共存

    目的:实现keil4和keil5的共存 1. Keil4 主要用来开发 C51 程序 2. Keil5 也就是 MDK 主要用来开发 ARM 芯片,如 STM32 系列芯片 3. 资料下载地址:链接: ...

  2. MongoDB 系统分析器

    1.1 系统分析器作用 可以利用系统分析器(system profiler)来查找耗时过长的操作. 系统分析器可记录特殊集合system.profile中的操作,并提供大量有关耗时长的操作信息,但相应 ...

  3. 二十三.Subversion基本操作、使用Subversion协同工作、制作nginx的RPM包

    1.Subversion基本操作 web1 1.1 安装Subversion服务器 ]# yum -y install subversion 1.1.1 创建版本库 ]# mkdir /var/svn ...

  4. 自定义starter

    https://github.com/deadzq/spring-boot-starter-hello 父子项目 子项目引用父项目中的依赖和配置参数

  5. vue 的 watch 如何在初始化时执行

    之前的做法一直是在 created 钩子之后手动调用一次 created() { this.fetchText(); }, watch: { text: 'fetchText', } 后来在翻阅文档的 ...

  6. Manthan, Codefest 19

    目录 Contest Info Solutions A. XORinacci B. Uniqueness C. Magic Grid D. Restore Permutation E. Let The ...

  7. (5)打鸡儿教你Vue.js

    条件与循环 条件判断使用 v-if 指令 <p v-if="seen"> <template v-if="ok"> <script ...

  8. epoll事件模型

    事件模型 EPOLL事件有两种模型: Edge Triggered (ET) 边缘触发只有数据到来才触发,不管缓存区中是否还有数据. Level Triggered (LT) 水平触发只要有数据都会触 ...

  9. 爬虫前提——正则表达式语法以及在Python中的使用

    正则表达式是用来处理字符串的强大工具,他并不是某种编程云. 正则表达式拥有独立的承受力引擎,不管什么编程语言,正则表达式的语法都是一样的. 正则表达式的匹配过程 1.一次拿出表达式和文本中的字符比较. ...

  10. 【spark 算子案例】

    package spark_example01; import java.io.File; import java.io.FileWriter; import java.io.IOException; ...