1. Bresenham算法核心:(详细原理见末尾)
- 理解光栅化:像素点只能是整数点。
- 借助决策变量 的正负号判断下一个点坐标,从而避免了计算直线斜率所用乘除法,只需要用加减法。
- 默认斜率绝对值在区间(0,1)时,即abs(dx)>abs(dy),步进方向为x方向,直线从左往右画。
- 默认斜率绝对值大于1时,即abs(dy)>abs(dx),步进方向为y方向,直线从下往上画。
- 可以这么理解直线画的方向:步进为x方向时,就往x增大的方向画;步进为y方向时,就往y增大的方向画。斜率绝对值为1的话,不会出现小数的情况,一般不考虑。
2. 算法思想:(以斜率在区间(0,1)中为例)
2.1. 画第一个点(x,y)即两个点中x坐标最小的点, 计算dx=x2-x1, dy=y2-y1, p1=2*abs(dy)-abs(dx).
2.2. 计算下一个点:x=x+1 ; y分类讨论。
2.3. p(i)>=0时,y(i+1)=y(i)+1并且p(i+1)=p(i)+2*(abs(dy)-abs(dx))。
2.4. 否则,y(i+1)=y(i)并且p(i+1)=p(i)+2*abs(dy).
2.5. 循环2-4步即可。
3. 源码展示:
#include<iostream>
#include<graphics.h>
#include<math.h>
#include<conio.h>
using namespace std;
#define x0 400
#define y0 300 //定义全局变量x0,y0:坐标轴中心(x0,y0)
void Bresenham(int x1, int y1, int x2, int y2) {
int x, y, dx, dy, p1, i;
dx = x2 - x1; //dx,dy有可能为负数
dy = y2 - y1;
if (abs(dx) > abs(dy)) { //斜率绝对值在(0,1),步进方向为x轴
if (x1<x2){ //默认画点从左往右画(左和右是针对x方向)
x = x1;
y = y1;
}
else
{
x = x2;
y = y2;
}
//cout << "(" << x << "," << y << ")" << endl;
putpixel(x+x0, y0-y, RED);
Sleep(50);
p1 = 2 * abs(dy) - abs(dx); //计算初始pi的值
for (i = min(x1,x2); i <max(x1,x2); i++) {
x = x + 1;
if (p1 >= 0) {
if (dx*dy >= 0)
y = y + 1;
else
y = y - 1; //若Pi>=0,y(i+1)=y(i)±1
p1 = p1 + 2 * (abs(dy) - abs(dx)); //更新pi
}
else {
y = y; //若Pi<0,y(i+1)=y(i)
p1 = p1 + 2 * abs(dy); //更新pi
}
//cout << "("<< x<<","<< y<<")" << endl;
putpixel(x + x0, y0 - y, RED);
Sleep(50);
}
}
else {
if (y1<y2) { //步进方向为y轴,默认画点从下往上画(下和上是针对y方向的)
x = x1;
y = y1;
}
else {
x = x2;
y = y2;
}
//cout << "(" << x << "," << y << ")" << endl;
putpixel(x + x0, y0 - y, RED);
Sleep(50);
p1 = 2 * abs(dx) - abs(dy);
for (i = min(y1,y2); i <max(y1,y2); i++) {
y = y + 1;
if (p1 >= 0) {
if (dx*dy>=0) //判断x方向是增加还是减少,很关键
x = x + 1;
else
x = x - 1;
p1 = p1 + 2 * (abs(dx) - abs(dy));
}
else {
x = x;
p1 = p1 + 2 * abs(dx);
}
//cout << "(" << x << "," << y << ")" << endl;
putpixel(x + x0, y0 - y, RED);
Sleep(50);
}
}
}
int main() {
int x1, x2, y1, y2;
cout << "请输入两个整数点的坐标(x1,y1),(x2,y2)" << endl;
cin >> x1 >> y1 >> x2 >> y2;
initgraph(x0 * 2, y0 * 2); //初始化图形窗口大小
line(0, y0, x0 * 2, y0); //坐标轴X
line(x0, 0, x0, y0 * 2); //坐标轴Y
Bresenham(x1, y1, x2, y2); //Bresenham画线算法
_getch(); //等待一个任意输入结束
closegraph(); //关闭图形窗口
return 0;
}
4. 结果展示:
4.1. 键盘输入 400 300 0 0
4.2. 直线动态画出,给出最后结果
5.代码心得:
- 画直线之前先用文本输出测试结果的正确性。
- 增加全局变量x0,y0,将其设定为坐标轴中心坐标
- Bresenham算法跟DDA算法相比不用浮点数,只用整数。
- Bresenham算法不用计算斜率,不用做除法。
- 注意不同的输入点的情况考虑要完整,测试文本选择不同的输入类型(尤其是正负号数据的不同组合以及先后顺序)类型。
- 代码以及讲述有误之处,欢迎指正。
PS -- Bresenham算法详细介绍:
通过一堆数学关系的推导从而简化DDA算法,可参考博客:https://blog.csdn.net/cjw_soledad/article/details/78886117(本来计划自己打上去的,无奈数学表达式太多,不想打公式)