计算机图形学实验(二)—— 直线Bresenham算法源码

 1. Bresenham算法核心:(详细原理见末尾)

  • 理解光栅化:像素点只能是整数点。
  • 借助决策变量 \large p_{i}的正负号判断下一个点坐标,从而避免了计算直线斜率所用乘除法,只需要用加减法。
  • 默认斜率绝对值在区间(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(本来计划自己打上去的,无奈数学表达式太多,不想打公式)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值