P1, P2 为要画线段的起终点,P1 = (x1, y1),P2 = (x2, y2)
∆x = x2-x1, ∆y = y2-y1,
m代表直线斜率0<m<1, m = ∆y/∆x
直线议程 y = mx+b
假
设
当
前
已
经
画
到
点
P
c
(
x
k
,
y
k
)
,
则
下
一
个
点
只
有
2
种
可
能
(
x
k
+
1
,
y
k
)
或
(
x
k
+
1
,
y
k
+
1
)
y
是
直
线
上
x
k
+
1
所
对
应
实
际
的
坐
标
y
=
m
(
x
k
+
1
)
+
b
令
d
u
p
p
e
r
=
y
k
+
1
−
y
=
y
k
+
1
−
m
(
x
k
+
1
)
−
b
d
l
o
w
e
r
=
y
−
y
k
=
m
(
x
k
+
1
)
+
b
−
y
k
d
l
o
w
e
r
−
d
u
p
p
e
r
=
2
m
(
x
k
+
1
)
−
2
y
k
+
2
b
−
1
p
k
是
决
策
参
数
令
p
k
=
∆
x
(
d
l
o
w
e
r
−
d
u
p
p
e
r
)
,
由
于
m
=
∆
y
/
∆
x
,
代
入
式
子
p
k
=
2
∆
y
(
x
k
+
1
)
−
2
∆
x
∗
y
k
+
2
∆
x
∗
b
−
∆
x
=
2
∆
y
∗
x
k
−
2
∆
x
∗
y
k
+
2
∆
y
+
∆
x
(
2
b
−
1
)
当
p
k
<
0
时
,
说
明
y
k
离
y
更
近
,
就
取
y
k
;
否
则
,
y
k
+
1
离
y
更
近
,
取
y
k
+
1.
假设当前已经画到点 Pc(x_k, y_k), 则下一个点只有2种可能(x_k+1, y_k) 或(x_k+1, y_k+1)\\ y 是直线上x_k+1 所对应实际的坐标\\y=m (x_k+1)+b\\ 令d_{upper} = y_k+1 - y = y_k+1-m(x_k+1)-b\\d_{lower} = y-y_k = m (x_k+1)+b-y_k \\ d_{lower}-d_{upper} = 2m(x_k+1)-2y_k+2b-1\\ p_k是决策参数 \\ 令p_k = ∆x(d_{lower}-d_{upper}), 由于m = ∆y/∆x , 代入式子\\p_k = 2∆y(x_k+1)-2∆x*y_k+2∆x*b-∆x \\ = 2∆y*x_k-2∆x*y_k+2∆y+∆x(2b-1)\\当p_k<0时,说明y_k离y更近,就取y_k; 否则,y_k+1离y更近,取y_k+1.
假设当前已经画到点Pc(xk,yk),则下一个点只有2种可能(xk+1,yk)或(xk+1,yk+1)y是直线上xk+1所对应实际的坐标y=m(xk+1)+b令dupper=yk+1−y=yk+1−m(xk+1)−bdlower=y−yk=m(xk+1)+b−ykdlower−dupper=2m(xk+1)−2yk+2b−1pk是决策参数令pk=∆x(dlower−dupper),由于m=∆y/∆x,代入式子pk=2∆y(xk+1)−2∆x∗yk+2∆x∗b−∆x=2∆y∗xk−2∆x∗yk+2∆y+∆x(2b−1)当pk<0时,说明yk离y更近,就取yk;否则,yk+1离y更近,取yk+1.
到此为止我们已经得到下一个点的决策参数。只要依次计算就可以得到所线上的坐标。但是pk的计算中有很多乘法,比较耗时,我们可以通过步进法将其转化成加法来做。具体如下:
将
x
k
+
1
,
y
k
+
1
代
入
式
子
,
可
得
到
p
k
+
1
=
2
∆
y
(
x
k
+
1
)
−
2
∆
x
∗
y
k
+
1
+
2
∆
x
∗
b
−
∆
x
x
k
+
1
=
x
k
+
1
则
p
k
+
1
−
p
k
=
2
∆
y
−
2
∆
x
(
y
k
+
1
−
y
k
)
移
项
得
p
k
+
1
=
p
k
+
2
∆
y
−
2
∆
x
(
y
k
+
1
−
y
k
)
,
这
样
我
们
就
得
到
p
k
的
递
推
式
。
p
k
+
1
=
p
k
+
2
∆
y
+
{
−
2
∆
x
,
p
k
>
=
0
0
,
p
k
<
0
上
述
算
法
中
2
∆
x
,
2
∆
y
都
可
以
事
先
算
好
,
后
续
只
要
用
到
加
法
即
可
将x_{k+1}, y_{k+1}代入式子,可得到\\p_{k+1}=2∆y(x_{k+1})-2∆x*y_{k+1}+2∆x*b-∆x \\ x_{k+1}=x_k+1则p_{k+1}-p_k = 2∆y-2∆x(y_{k+1}-y_k) \\移项得 p_{k+1} =p_k+ 2∆y-2∆x(y_{k+1}-y_k), \\ 这样我们就得到p_k的递推式。\\ p_{k+1} =p_k+ 2∆y+\begin{cases} -2∆x, p_k>=0\\ 0, p_k<0\end{cases}\\上述算法中2∆x, 2∆y都可以事先算好,后续只要用到加法即可
将xk+1,yk+1代入式子,可得到pk+1=2∆y(xk+1)−2∆x∗yk+1+2∆x∗b−∆xxk+1=xk+1则pk+1−pk=2∆y−2∆x(yk+1−yk)移项得pk+1=pk+2∆y−2∆x(yk+1−yk),这样我们就得到pk的递推式。pk+1=pk+2∆y+{−2∆x,pk>=00,pk<0上述算法中2∆x,2∆y都可以事先算好,后续只要用到加法即可
p0计算
将
(
x
0
,
y
0
)
代
入
p
k
表
达
式
得
:
p
0
=
2
∆
y
∗
x
0
−
2
∆
x
∗
y
0
+
2
∆
y
+
∆
x
(
2
b
−
1
)
又
因
为
y
0
=
m
∗
x
0
+
b
=
∆
y
/
∆
x
∗
x
0
+
b
,
代
入
上
式
得
:
p
0
=
2
∆
y
∗
x
0
−
2
∆
x
∗
(
∆
y
/
∆
x
∗
x
0
+
b
)
+
2
∆
y
+
∆
x
(
2
b
−
1
)
=
2
∆
y
∗
x
0
−
2
∆
y
∗
x
0
−
2
∆
x
∗
b
+
2
∆
y
+
∆
x
(
2
b
−
1
)
=
2
∆
y
−
∆
x
将(x_0,y_0)代入p_k表达式得:\\ p_0 = 2∆y*x_0-2∆x*y_0+2∆y+∆x(2b-1) \\ 又因为 y_0 = m*x_0+b= ∆y/∆x*x_0+b, 代入上式得:\\ p_0 = 2∆y*x_0-2∆x*(∆y/∆x*x_0+b)+2∆y+∆x(2b-1) \\=2∆y*x_0-2∆y*x_0-2∆x*b+2∆y+∆x(2b-1)\\ = 2∆y-∆x
将(x0,y0)代入pk表达式得:p0=2∆y∗x0−2∆x∗y0+2∆y+∆x(2b−1)又因为y0=m∗x0+b=∆y/∆x∗x0+b,代入上式得:p0=2∆y∗x0−2∆x∗(∆y/∆x∗x0+b)+2∆y+∆x(2b−1)=2∆y∗x0−2∆y∗x0−2∆x∗b+2∆y+∆x(2b−1)=2∆y−∆x
上述只是讲了0<m<1的算法,对于-1<m<0可以直接对称过去。
对于|m|>1的情况可以把x,y 互换。
对于m=0, 无穷大的情况也适用。
代码实现
#include "glew/2.2.0_1/include/GL/glew.h" #include "glfw/3.3.4/include/GLFW/glfw3.h" #include <iostream> using namespace std; void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode) { //如果按下ESC,把windowShouldClose设置为True,外面的循环会关闭应用 if(key==GLFW_KEY_ESCAPE && action == GLFW_PRESS) glfwSetWindowShouldClose(window, GL_TRUE); std::cout<<"ESC"<<mode; } class Point { public: int x, y; Point(int xx, int yy):x(xx), y(yy){} }; void setPixel(Point p) { // cout<<p.x<<","<<p.y<<endl; glBegin(GL_POINTS); glVertex2i(p.x, p.y); glEnd(); glFlush(); } /* * 简单Bresenham 算法 * 只处理|m|>1 */ void LineBres_2(Point p1, Point p2) { // 保持从下往上画 if(p2.y<p1.y) { Point t = p2; p2=p1; p1=t; } int deltaX = abs(p2.x-p1.x), deltaY = abs(p2.y-p1.y); int p0 = 2*deltaX-deltaY; int twDeltaX = 2*deltaX, twDeltaXmTwoDeltaY = 2*deltaX-2*deltaY; int step = 0; if(deltaX>0) step = (p2.x-p1.x) / deltaX; setPixel(p1); for (; p1.y < p2.y;) { p1.y++; if(p0<0) { p0+=twDeltaX; } else { p0+=twDeltaXmTwoDeltaY; p1.x+=step; } setPixel(p1); cout<<p1.x<<","<<p1.y<<endl; } } /* * 简单Bresenham 算法 * 只处理|m|<=1 */ void LineBres_1(Point p1, Point p2) { // 保持从左到右画 if(p2.x<p1.x) { Point t = p2; p2=p1; p1=t; } int deltaX = abs(p2.x-p1.x), deltaY = abs(p2.y-p1.y); if(deltaX<deltaY) { LineBres_2(p1, p2); return; } int p0 = 2*deltaY-deltaX; int twDeltaY = 2*deltaY, twDeltaYmTwoDeltaX = 2*deltaY-2*deltaX; int step = 0; if(deltaY>0) step = (p2.y-p1.y) / deltaY; setPixel(p1); for (; p1.x < p2.x;) { p1.x++; if(p0<0) { p0+=twDeltaY; } else { p0+=twDeltaYmTwoDeltaX; p1.y+=step; } setPixel(p1); cout<<p1.x<<","<<p1.y<<endl; } } int main(void) { //初始化GLFW库 if (!glfwInit()) return -1; //创建窗口以及上下文 GLFWwindow *window = glfwCreateWindow(400, 400, "hello world", NULL, NULL); if (!window) { //创建失败会返回NULL glfwTerminate(); } //建立当前窗口的上下文 glfwMakeContextCurrent(window); glfwSetKeyCallback(window, key_callback); //注册回调函数 //glViewport(0, 0, 400, 400); gluOrtho2D(-200, 200.0, -200, 200.0); //循环,直到用户关闭窗口 cout<<123<<endl; while (!glfwWindowShouldClose(window)) { /*******轮询事件*******/ glfwPollEvents(); // cout<<456<<endl; //选择清空的颜色RGBA glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT); // glColor3f(0,0, 0); glMatrixMode(GL_PROJECTION); LineBres_1(Point(0,0),Point(100,70)); LineBres_1(Point(0,0),Point(100,-70)); LineBres_1(Point(0,0),Point(70,100)); LineBres_1(Point(0,0),Point(-70,100)); LineBres_1(Point(0,0),Point(-100,70)); LineBres_1(Point(0,0),Point(-70,-100)); LineBres_1(Point(0,0),Point(-100,-70)); LineBres_1(Point(0,0),Point(70,-100)); LineBres_1(Point(0,0),Point(0,100)); LineBres_1(Point(0,0),Point(0,-100)); LineBres_1(Point(0,0),Point(100,0)); LineBres_1(Point(0,0),Point(-100,0)); LineBres_1(Point(0,0),Point(0,0)); /******交换缓冲区,更新window上的内容******/ glfwSwapBuffers(window); //break; } glfwTerminate(); return 0; }
未完待续。。。。