简介:
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网址:
补充知识点:
宏:
c++deg2rad函数