使用Bresenham算法画一个三角形边框
根据要求,设计一个三角形的类,如下。在构造函数中输入三个顶点坐标。使用draw函数画图。
1 | class Triangle { |
draw函数初始化缓冲区,然后调用drawLine函数画三角形的三条边:
1 | void Triangle::draw() { |
drawLine函数根据两个端点坐标,使用Bresenham算法计算直线上每一个点的坐标,然后调用drawPoint函数画点。步骤如下:
输入数据
(x0, y0)为起点坐标,(xn, yn)为终点坐标。
1
void Triangle::drawLine(int x0, int y0, int xn, int yn)
计算Δx和Δy,判断Δx和Δy是否为0
如果Δx和Δy都为0,说明两点重合,只画一个点;如果Δx为0,则画一条垂直的线;如果Δy为0,则画一条水平的线。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19int delta_x = abs(xn - x0), delta_y = abs(yn - y0);
int x = x0, y = y0;
if (delta_x == 0 && delta_y == 0) {
drawPoint(x, y, 0, color);
}
else if (delta_x == 0) {
int y_dir = (yn - y0) / delta_y;
for (int i = 0; i <= delta_y; i++) {
drawPoint(x, y, 0, color);
y += y_dir;
}
}
else if (delta_y == 0) {
int x_dir = (xn - x0) / delta_x;
for (int i = 0; i <= delta_x; i++) {
drawPoint(x, y, 0, color);
x += x_dir;
}
}如果Δx和Δy均不为0,则使用Bresenham算法画直线
斜率m的绝对值小于或等于1时,每次移动x坐标一个单位,然后计算y坐标。(根据端点坐标确定移动方向)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16else if ((float)delta_y / (float)delta_x <= 1) {
int x_dir = (xn - x0) / delta_x;
int y_dir = (yn - y0) / delta_y;
int p = 2 * delta_y - delta_x;
for (int i = 0; i <= delta_x; i++) {
drawPoint(x, y, 0, color);
x += x_dir;
if (p <= 0) {
p += (2 * delta_y);
}
else {
y += y_dir;
p += (2 * delta_y - 2 * delta_x);
}
}
}斜率m的绝对值大与1时,每次移动y坐标一个单位,然后计算x坐标。(根据端点坐标确定移动方向)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16else {
int x_dir = (xn - x0) / delta_x;
int y_dir = (yn - y0) / delta_y;
int p = 2 * delta_x - delta_y;
for (int i = 0; i <= delta_y; i++) {
drawPoint(x, y, 0, color);
y += y_dir;
if (p <= 0) {
p += (2 * delta_x);
}
else {
x += x_dir;
p += (2 * delta_x - 2 * delta_y);
}
}
}
drawPoint函数会根据窗口大小,将输入的坐标值转换成-1~1之间的标准坐标值,然后绘制一个点:
1 | void Triangle::drawPoint(int x, int y, int z, float color[3]) { |
最后在ImGui设置三个点的坐标,就可以画出三角形
使用Bresenham算法画一个圆
根据要求,设计一个圆的类,如下。在构造函数中输入圆心坐标和半径。使用draw函数画图。
1 | class Circle { |
draw函数初始化缓冲区,使用Bresenham算法计算1/8圆周的坐标,然后调用drawSymmetricPoints画出所有8个对称点。计算时,x坐标每次增加1,然后计算y坐标的值。
1 | int x = x0, y = y0 + r, d = 1 - r; |
drawSymmetricPoints函数计算8个关于圆心对称的点,然后调用drawPoint函数画点。drawPoint函数与画三角形的drawPoint函数相同。
1 | void Circle::drawSymmetricPoints(int x, int y) { |
最后在ImGui设置圆心坐标和半径,就可以画出圆。
在GUI在添加菜单栏,可以选择是三角形边框还是圆,以及能调整圆的大小
在菜单栏中绑定bool对象,实现选择:
1 | if (ImGui::BeginMainMenuBar()) { |
圆的参数由ImGuid的滑块传入,因此可以调整圆的大小:
1 | if (cir) { |
Bonus:使用三角形光栅转换算法,用和背景不同的颜色,填充你的三角形
给三角形类添加两个方法。fill方法用来填充三角形,contain方法用来判断一个点是否在三角形内部。
fill函数中,先对顶点坐标进行排序,得到三角形的包围盒。然后遍历包围盒内的点,调用contain判断点是否在内部,最后调用drawPoint画点即可。
1 | for (int x = x_axis[0]; x <= x_axis[2]; x++) { |
contain函数使用向量的向量积来判断点是否在三角形内部。以该点为起点,分别以三个顶点为终点,可以得到三个向量。按照顺序两两求向量积,得到三个乘积。如果三个乘积同号,则说明点在三角形内部。
1 | bool Triangle::contain(int x, int y) { |
填充结果