#include<iostream>
#include<graphics.h>
#include<conio.h>
#include<cmath>
#include<windows.h>
#define PI 3.1415926
using namespace std;
//优化后的Bresenham算法,能够处理斜率不存在,斜率为负数,斜率绝对值大于一等情况,并且可以根据需要自动调整两点顺序,具有良好的通用性
//使用只需要输入两点的横纵坐标值即可
void Bresenham(const int& x1, const int& y1, const int& x2, const int& y2)
{
int dx = x2 - x1, dy = y2 - y1;
enum MainChangeDirection { x, y };//用一个枚举变量记录直线的主要变化方向(x方向或者y方向)
MainChangeDirection Direction;
if (abs(dx) >= abs(dy))
Direction = x;
else Direction = y;
if ((Direction == x && x1 > x2) || (Direction == y && y1 > y2))//如果在主要变化方向上的次序相反了,则递归调用自身即可
{
Bresenham(x2, y2, x1, y1);
return;
}
if (dx == 0)//处理斜率不存在的特殊情况
{
for (int y = y1; y <= y2; ++y)
{
putpixel(x1, y, RED);
}
Sleep(1);
return;
}
double k = double(dy) / dx;
if (Direction == x)//处理主要变化方向为x轴的情况
{
int x = x1, y = y1;
if (k >= 0)//主要变化方向为x轴并且斜率大于等于零
{
int e = -dx;
for (; x <= x2; ++x)
{
putpixel(x, y, RED);
e += 2 * dy;
if (e > 0)
{
y++;
e -= 2 * dx;
}
}
}
else if (k < 0)//主要变化方向为x轴且斜率小于零
{
int e = dx;
for (; x <= x2; ++x)
{
putpixel(x, y, RED);
e += 2 * dy;
if (e <= 0)
{
y--;
e += 2 * dx;
}
}
}
}
else if (Direction == y)
{
int y = y1, x = x1;
if (k >= 0)//主要变化方向为y轴方向并且斜率大于等于零
{
int e = -dy;
for (; y <= y2; ++y)
{
putpixel(x, y, RED);
e += 2 * dx;
if (e > 0)
{
x++;
e -= 2 * dy;
}
}
}
else if (k < 0)//主要变化方向为y轴方向并且斜率小于零
{
int e = dy;
for (; y <= y2; ++y)
{
putpixel(x, y, RED);
e += 2 * dx;
if (e <= 0)
{
x--;
e += 2 * dy;
}
}
}
}
Sleep(1);
return;
}
void WideLine(const int& x1, const int& y1, const int& x2, const int& y2, const int& width)
{
int dx = x2 - x1, dy = y2 - y1;
enum MainChangeDirection { x, y };//用一个枚举变量记录直线的主要变化方向(x方向或者y方向)
MainChangeDirection Direction;
if (abs(dx) >= abs(dy))
Direction = x;
else Direction = y;
if ((Direction == x && x1 > x2) || (Direction == y && y1 > y2))//如果在主要变化方向上的次序相反了,则递归调用自身即可
{
WideLine(x2, y2, x1, y1, width);
return;
}
int mid_width = width >> 1;
if (dx == 0)//处理斜率不存在的特殊情况
{
for (int y = y1; y <= y2; ++y)
{
Bresenham(x1 - mid_width, y, x1 + mid_width, y);
}
return;
}
if (dy == 0)//处理斜率为零的特殊情况
{
for (int x = x1; x <= x2; x++)
{
Bresenham(x, y1 - mid_width, x, y1 + mid_width);
}
return;
}
double k = double(dy) / dx;
if (Direction == x)//处理主要变化方向为x轴的情况
{
int x = x1, y = y1;
if (k >= 0)//主要变化方向为x轴并且斜率大于等于零
{
int e = -dx;
for (; x <= x2; ++x)
{
int x_range = fabs(cos(atan(k))) * mid_width;
int y_range = fabs(sin(atan(k))) * mid_width;
int start_x, start_y, end_x, end_y;
start_x = x - x_range;
end_x = x + x_range;
start_y = y + y_range;
end_y = y - y_range;
Bresenham(start_x, start_y, end_x, end_y);
e += 2 * dy;
if (e > 0)
{
y++;
e -= 2 * dx;
}
}
}
else if (k < 0)//主要变化方向为x轴且斜率小于零
{
int e = dx;
for (; x <= x2; ++x)
{
int x_range = fabs(cos(atan(k) + PI / 2)) * mid_width;
int y_range = fabs(sin(atan(k) + PI / 2)) * mid_width;
int start_x, start_y, end_x, end_y;
start_x = x - x_range;
end_x = x + x_range;
start_y = y - y_range;
end_y = y + y_range;
Bresenham(start_x, start_y, end_x, end_y);
e += 2 * dy;
if (e <= 0)
{
y--;
e += 2 * dx;
}
}
}
}
else if (Direction == y)
{
int y = y1, x = x1;
if (k >= 0)//主要变化方向为y轴方向并且斜率大于等于零
{
int e = -dy;
for (; y <= y2; ++y)
{
int x_range = fabs(cos(atan(k))) * mid_width;
int y_range = fabs(sin(atan(k))) * mid_width;
int start_x, start_y, end_x, end_y;
start_x = x - x_range;
end_x = x + x_range;
start_y = y + y_range;
end_y = y - y_range;
Bresenham(start_x, start_y, end_x, end_y);
e += 2 * dx;
if (e > 0)
{
x++;
e -= 2 * dy;
}
}
}
else if (k < 0)//主要变化方向为y轴方向并且斜率小于零
{
int e = dy;
for (; y <= y2; ++y)
{
int x_range = fabs(cos(atan(k))) * mid_width;
int y_range = fabs(sin(atan(k))) * mid_width;
int start_x, start_y, end_x, end_y;
start_x = x - x_range;
end_x = x + x_range;
start_y = y + y_range;
end_y = y - y_range;
Bresenham(start_x, start_y, end_x, end_y);
if (e <= 0)
{
x--;
e += 2 * dy;
}
}
}
}
return;
}
//以下是用来衔接两带宽度直线的“转弯”算法,相当于在指定点以半宽度作一个圆
void Turning(const int& x, const int& y, const int& width)
{
int mid_width = width / 2;
double angle;
int dx, dy;
for (double i = 0; i <= 360; i++)
{
angle = i / 180 * PI;
dx = cos(angle) * mid_width;
dy = sin(angle) * mid_width;
Bresenham(x, y, x + dx, y + dy);
}
}
//这是本次实验的核心函数,对于任意两个点,先判断是否需要进行转向,之后连接下一个点
void Draw_WideLines(const int* x, const int* y, const int& n, const int& width)
{
initgraph(800, 640);
for (int i = 0; i < n - 1; i++)
{
if (i != 0)
{
Turning(x[i], y[i], width);
}
WideLine(x[i], y[i], x[i + 1], y[i + 1], width);
}
_getch();
closegraph();
}
int main(void)
{
while (true)
{
int cmd;
cout << "输入1开始程序,输入2退出程序:" << endl;
cin >> cmd;
if (cmd == 2)
{
cout << "程序已经退出" << endl;
break;
}
cout << "绘图程序开始..." << endl;
int num;
cout << "请输入点的个数:";
cin >> num;
int* xArray = new int[num];
int* yArray = new int[num];
cout << "请输入这些点的横坐标:" << endl;
for (int i = 0; i < num; i++)
{
cin >> xArray[i];
}
cout << "请输入这些点的纵坐标:" << endl;
for (int i = 0; i < num; i++)
{
cin >> yArray[i];
}
int width;
cout << "请输入线条的宽度:";
cin >> width;
cout << "参数已经确认,按任意键可开始绘图,绘图结束后按任意键可以返回..." << endl;
_getch();
Draw_WideLines(xArray, yArray, num, width);
}
return 0;
}
注:头文件的库中<graphics.h>需要自行免费下载