A-2 绘制直线Brensenham算法证明与实现

简介:

Bresenhan算法将坐标系分割成棋盘形状,每个像素占有一个棋格,当我们进行采样时(直线斜率小于1),如下图所示,假设给定绘图的起始点为(10,11),那么绘制下一个采样点的坐标必然是从(11,11)和(11,12)中选择一个。如果把这种情况一般化,对于绘制直线的起始点是(Xk,Yk),那么其下一个采样点必然是(Xk+1,Yk)或者(Xk+1,Yk+1)中的一个。

作用:消灭浮点数。因为浮点数是不准确的。不多解释,算法并不复杂。


 证明:

(1)思路

几个关键点: 

这是一些定义和很显然的结论,但是是我们理解这个算法的基础。 

构建  p_i =  dx*(d_1-d_2) ,这个式子  是 根据影响步进的因素构造出来的(至于这个式子的构造,其实大有文章的,这块不必深究)。所以dx 的正负和 (d_1-d_2)的正负,都影响着p_i的正负,所以只要p_i的正负知道了,我们就知道步进的策略是什么.。不是构建p_i难理解,而是如果想出这个式子的过程不必理解。看待问题的角度要犀利!

下面推导了 p_(i+1)-p_i就是保证这个公式能够递归下去。后面又把p_1和p_2带入检验。检验一下。

步进:分成方向和长度。向x方向步进,但是长度步进加到y上。向x方向步进,但是长度加到x上。其实也是y。只不过是x,y再决定偏移量的时候给换了。

(2)好处是:把八分之一象限的公式推广到全坐标系。四像素格图中我们容易看出步进有四种可能性。分别是:一类是点pt1到点pt2,dis/dis+1;     另一类 点pt2到点pt1,  dis+1/dis.这里其实有8中可能。因为x也可以跟着y动。所以看x坐标,与y坐标改变刻板印象,比如镜像角度。

反向:-1   x,y翻转。

代码便签:

1.求出x,y的偏移量disX,disY,设定x,y点的初始值xNow,yNow。

2.判断步进长度是+1还是-1,根据x,y的偏移量的正负。

3.判断步进方向是disX还是disY。比较disX与disY的大小。谁大用谁的方向步进。

标签法:假设用是bool 变量来定义useXStep,如果disY>disX,就把sumStep换成disY,useXStep=false。

4.初始化p_i值。int p = 2 * disY - disX;

5.开始绘制一个个的点,步进主坐标。p_i>0,X方向步进,p_i要迭代,故维护好p_i。

6.在主函数的render函数里调用。.完善函数接口。


源代码:

//Canvas.cpp
void Canvas::drawLine(intV2 pt1, intV2 pt2, RGBA _color)
	{
        //---------------------------------<1>
		int disY = abs(pt2.y - pt1.y);
		int disX = abs(pt2.x - pt1.x);

		int xNow = pt1.x;
		int yNow = pt1.y;          

		//判断步进的长度---------------------<2>
		int stepX = (pt1.x - pt2.x )< 0 ? 1 : -1;
		int stepY = (pt1.y- pt2.y)<0 ? 1:-1;

		//判断步进的方向---------------------<3>
		int sumStep = disX;
		bool useXStep = true;
		if (disX < disY)
		{
			sumStep = disY;
			useXStep = false;
			SWAP_INT(disX, disY);
		}
		//初始化P值-------------------------<4> 
		int p = 2 * disY - disX;
		//逐步画点
		for (int i = 0; i < sumStep; i++)
		{
			Canvas::drawPoint(xNow, yNow, _color);
			if (p >= 0)--------------------<5>x方向步进,y上变化
			{
				if (useXStep)
				{
					yNow += stepY;
				}
				else {
					xNow += stepX;
				}
				p = p - 2 * disX;
			}
			//步进主坐标---------------------<6>
			if (useXStep)
			{
				xNow += stepX;
			}
			else {
				yNow += stepY;
			}
			p = p + 2 * disY;
		}	
	}	
//主函数.cpp
void Render()//全局绘制函数
{
    _canvas->clear();
    //指向Canvas类的一个指针,调用了drawLine函数。
   _canvas->drawLine(GT::intV2(100, 100), GT::intV2(150, 180), GT::RGBA(255, 0, 0));
    BitBlt(hDC, 0, 0, wWidth, wHeight, hMem, 0, 0, SRCCOPY);
}
第一象限效果图
//主函数.cpp

void Render()       //全局绘制函数
{
    _canvas->clear();
    //指向Canvas类的一个指针,调用了drawLine函数。
    GT::RGBA _color(rand() % 255, rand() % 255, 0);
    GT::intV2 pt1(100, 100);  float dis = 50;
    for (int i = 0; i < 360; i += 20)
    {
        float radian = DEG2RAD(i);
        int x = dis * sin(radian) + pt1.x;
        int y = dis * cos(radian) + pt1.y;
        GT::intV2  pt2(x, y);
        _canvas->drawLine(pt1, pt2, _color);
    }
    BitBlt(hDC, 0, 0, wWidth, wHeight, hMem, 0, 0, SRCCOPY);
}  
各个象限效果图

源代码gittee网址:

图形学/画线/01 · 刘敬一如/C++_exersize - Gitee.comhttps://gitee.com/liu-jingyiru/c--exersize/blob/master/%E5%9B%BE%E5%BD%A2%E5%AD%A6/%E7%94%BB%E7%BA%BF/01


补充知识点:

宏:

c++deg2rad函数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘敬_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值