直线方程

假设在屏幕上面有两个点,P0(x0,y0)和P1(x1,y1),那么我们如何画一条从P0到P1的线?其实很简单,在P0P1这条线上的点P都满足一个方程:

P = P0 + t(P1 - P0)

转化为:

x = x0 + t(x1 - x0)
y = y0 + t(y1 - y0)

进一步化简第一个方程,求得:

t=(x - x0) / (x1 - x0)

把t带入第二个方程,可得:

y = y0 + (x - x0) / (x1 - x0) * (y1 - y0)

因为(y1 - y0) / (x1 - x0)是一个常量,咱们取为a(其实就是线的斜率),可得:

y = y0 + a * (x - x0)

进一步化简,可得:

y = ax + (y0 -ax0)

y0 - ax0其实就是线的截距,咱们取为b,可得:

y = ax + b

这个方程就是大家熟悉的直线方程了。

画线

  • 第一种画线方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 引用EasyX图形库头文件
#include <graphics.h>
#include <conio.h>
//glm数学相关头文件
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>


void DrawLine1(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
float a = (P1.y - P0.y) / (P1.x - P0.x);
float b = P0.y - a * P0.x;
for (int x=P0.x;x<P1.x;x++)
{
int y = a * x + b;
putpixel(x, y, color);
}
}

int main()
{
initgraph(640, 640); // 创建绘图窗口,大小为 640x480 像素

DrawLine1(glm::vec2(100,100), glm::vec2(400,400), RED);


_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}

结果
在这里插入图片描述
其实这个方法画出来的线是无法画垂直的线的,因为求a的时候P1.x-P0.x会等于零,除数是不能为零的(因为是浮点数所以可能并不会报错说进行了除零操作)。

  • 第二种画线方法

    y(x) = ax + b
    y(x+1) =a * (x + 1) + b

对第二个方程进行变形,可得:

y(x+1) = ax + a + b
y(x+1) = (ax+b) + a
y(x+1) = y(x) + a

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 引用EasyX图形库头文件
#include <graphics.h>
#include <conio.h>
//glm数学相关头文件
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>


void DrawLine1(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
float a = (P1.y - P0.y) / (P1.x - P0.x);
float b = P0.y - a * P0.x;
for (int x=P0.x;x<P1.x;x++)
{
int y = a * x + b;
putpixel(x, y, color);
}
}

void DrawLine2(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
float a = (P1.y - P0.y) / (P1.x - P0.x);
float y = P0.y;
for (int x = P0.x;x < P1.x;x++)
{
putpixel(x, y, color);
y = y + a;
}
}

int main()
{
initgraph(640, 640); // 创建绘图窗口,大小为 640x480 像素

DrawLine2(glm::vec2(100,100), glm::vec2(300,100), RED);


_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}

结果
在这里插入图片描述
第二种画线方法比第一种要快一点,不过和第一种一样也不能画垂线。

  • 第三种画线
    前面两种都是只能从左到右画线,而不能从右到左。要想摆脱这种限制也很简单,只需要交换一下顺序就可以了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// 引用EasyX图形库头文件
#include <graphics.h>
#include <conio.h>
//glm数学相关头文件
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <iostream>


void DrawLine1(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
float a = (P1.y - P0.y) / (P1.x - P0.x);
float b = P0.y - a * P0.x;
for (int x=P0.x;x<P1.x;x++)
{
int y = a * x + b;
putpixel(x, y, color);
}
}

void DrawLine2(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
float a = (P1.y - P0.y) / (P1.x - P0.x);
float y = P0.y;
for (int x = P0.x;x < P1.x;x++)
{
putpixel(x, y, color);
y = y + a;
}
}

void DrawLine3(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
if (P0.x > P1.x)
{
std::swap(P0, P1);
}
float a = (P1.y - P0.y) / (P1.x - P0.x);
float y = P0.y;
for (int x = P0.x;x < P1.x;x++)
{
putpixel(x, y, color);
y = y + a;
}
}

int main()
{
initgraph(640, 640); // 创建绘图窗口,大小为 640x480 像素

DrawLine3(glm::vec2(100,100), glm::vec2(300,100), RED);
DrawLine3(glm::vec2(500, 200), glm::vec2(100, 200), RED);

_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}

结果
在这里插入图片描述

  • 第四种画线方法
    其实如果想画垂线的话,也很容易,只需要把x和y换一下,只不过又不能画水平线了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
// 引用EasyX图形库头文件
#include <graphics.h>
#include <conio.h>
//glm数学相关头文件
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <iostream>


void DrawLine1(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
float a = (P1.y - P0.y) / (P1.x - P0.x);
float b = P0.y - a * P0.x;
for (int x=P0.x;x<P1.x;x++)
{
int y = a * x + b;
putpixel(x, y, color);
}
}

void DrawLine2(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
float a = (P1.y - P0.y) / (P1.x - P0.x);
float y = P0.y;
for (int x = P0.x;x < P1.x;x++)
{
putpixel(x, y, color);
y = y + a;
}
}

void DrawLine3(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
if (P0.x > P1.x)
{
std::swap(P0, P1);
}
float a = (P1.y - P0.y) / (P1.x - P0.x);
float y = P0.y;
for (int x = P0.x;x < P1.x;x++)
{
putpixel(x, y, color);
y = y + a;
}
}

void DrawLine4(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
if (P0.y > P1.y)
{
std::swap(P0, P1);
}
float a = (P1.x - P0.x) / (P1.y - P0.y);
float x = P0.x;
for (int y = P0.y;y < P1.y;y++)
{
putpixel(x, y, color);
x = x + a;
}
}

int main()
{
initgraph(640, 640); // 创建绘图窗口,大小为 640x480 像素

DrawLine3(glm::vec2(100,100), glm::vec2(500,100), RED);
DrawLine4(glm::vec2(100, 100), glm::vec2(100, 500), GREEN);

_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}

结果
在这里插入图片描述

  • 第五种画线方法
    要想画任何斜率的线其实只需要把上面的结合在一起就可以了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// 引用EasyX图形库头文件
#include <graphics.h>
#include <conio.h>
//glm数学相关头文件
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <iostream>


void DrawLine1(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
float a = (P1.y - P0.y) / (P1.x - P0.x);
float b = P0.y - a * P0.x;
for (int x=P0.x;x<P1.x;x++)
{
int y = a * x + b;
putpixel(x, y, color);
}
}

void DrawLine2(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
float a = (P1.y - P0.y) / (P1.x - P0.x);
float y = P0.y;
for (int x = P0.x;x < P1.x;x++)
{
putpixel(x, y, color);
y = y + a;
}
}

void DrawLine3(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
if (P0.x > P1.x)
{
std::swap(P0, P1);
}
float a = (P1.y - P0.y) / (P1.x - P0.x);
float y = P0.y;
for (int x = P0.x;x < P1.x;x++)
{
putpixel(x, y, color);
y = y + a;
}
}

void DrawLine4(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
if (P0.y > P1.y)
{
std::swap(P0, P1);
}
float a = (P1.x - P0.x) / (P1.y - P0.y);
float x = P0.x;
for (int y = P0.y;y < P1.y;y++)
{
putpixel(x, y, color);
x = x + a;
}
}

void DrawLine5(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
float dx = P1.x - P0.x;
float dy = P1.y - P0.y;
if (glm::abs(dx) > glm::abs(dy))
{
if (P0.x > P1.x)
{
std::swap(P0, P1);
}
float a = dy / dx;
float y = P0.y;
for (int x = P0.x;x < P1.x;x++)
{
putpixel(x, y, color);
y = y + a;
}
}
else
{
if (P0.y > P1.y)
{
std::swap(P0, P1);
}
float a = dx / dy;
float x = P0.x;
for (int y = P0.y;y < P1.y;y++)
{
putpixel(x, y, color);
x = x + a;
}
}

}

int main()
{
initgraph(640, 640); // 创建绘图窗口,大小为 640x480 像素

DrawLine5(glm::vec2(100,100), glm::vec2(500,100), RED);
DrawLine5(glm::vec2(100, 100), glm::vec2(100, 500), GREEN);

_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}

结果
在这里插入图片描述

  • 第六种画线方法
    这次我们在上一次的基础上添加一个插值函数。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// 引用EasyX图形库头文件
#include <graphics.h>
#include <conio.h>
//glm数学相关头文件
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

#include <iostream>
#include <vector>


void DrawLine1(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
float a = (P1.y - P0.y) / (P1.x - P0.x);
float b = P0.y - a * P0.x;
for (int x=P0.x;x<P1.x;x++)
{
int y = a * x + b;
putpixel(x, y, color);
}
}

void DrawLine2(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
float a = (P1.y - P0.y) / (P1.x - P0.x);
float y = P0.y;
for (int x = P0.x;x < P1.x;x++)
{
putpixel(x, y, color);
y = y + a;
}
}

void DrawLine3(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
if (P0.x > P1.x)
{
std::swap(P0, P1);
}
float a = (P1.y - P0.y) / (P1.x - P0.x);
float y = P0.y;
for (int x = P0.x;x < P1.x;x++)
{
putpixel(x, y, color);
y = y + a;
}
}

void DrawLine4(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
if (P0.y > P1.y)
{
std::swap(P0, P1);
}
float a = (P1.x - P0.x) / (P1.y - P0.y);
float x = P0.x;
for (int y = P0.y;y < P1.y;y++)
{
putpixel(x, y, color);
x = x + a;
}
}

void DrawLine5(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
float dx = P1.x - P0.x;
float dy = P1.y - P0.y;
if (glm::abs(dx) > glm::abs(dy))
{
if (P0.x > P1.x)
{
std::swap(P0, P1);
}
float a = dy / dx;
float y = P0.y;
for (int x = P0.x;x < P1.x;x++)
{
putpixel(x, y, color);
y = y + a;
}
}
else
{
if (P0.y > P1.y)
{
std::swap(P0, P1);
}
float a = dx / dy;
float x = P0.x;
for (int y = P0.y;y < P1.y;y++)
{
putpixel(x, y, color);
x = x + a;
}
}

}

std::vector<float> Interpolate(float i0, float d0, float i1, float d1)
{
std::vector<float> values;
if (glm::abs(i0 - i1)<=1e-6)
{
values.push_back(d0);
return values;
}
float a = (d1 - d0) / (i1 - i0);
float d = d0;
for (int i = i0;i < i1;i++)
{
values.push_back(d);
d = d + a;
}
return values;
}

void DrawLine6(glm::vec2 P0, glm::vec2 P1, COLORREF color)
{
if (glm::abs(P1.x-P0.x) > glm::abs(P1.y-P0.y))
{
if (P0.x > P1.x)
{
std::swap(P0, P1);
}
std::vector<float> ys = Interpolate(P0.x,P0.y,P1.x,P1.y);
for (int x = P0.x;x < P1.x;x++)
{
putpixel(x, ys[x-P0.x], color);
}
}
else
{
if (P0.y > P1.y)
{
std::swap(P0, P1);
}
std::vector<float> xs = Interpolate(P0.y, P0.x, P1.y, P1.x);
for (int y = P0.y;y < P1.y;y++)
{
putpixel(xs[y - P0.y], y, color);
}
}

}

int main()
{
initgraph(640, 640); // 创建绘图窗口,大小为 640x480 像素

DrawLine6(glm::vec2(100,100), glm::vec2(500,100), RED);
DrawLine6(glm::vec2(100, 100), glm::vec2(100, 500), GREEN);

_getch(); // 按任意键继续
closegraph(); // 关闭绘图窗口
return 0;
}

结果
在这里插入图片描述

结尾

其实画线的方法有很多,大家有兴趣的话可以去网上找相应的画线算法进行测试。咱们现在已经可以画线了,那么接下来就可以画三角形啦。

如果你遇到了问题可以在评论区告诉我哦
项目地址