#include<iostream>
#include<graphics.h>
#include<conio.h>
#include<windows.h>
#include<vector>
using namespace std;
static int wx1, wy1, wx2, wy2;
//优化后的Bresenham算法,能够处理斜率不存在,斜率为负数,斜率绝对值大于一等情况,并且可以根据需要自动调整两点顺序,具有良好的通用性
//使用只需要输入两点的横纵坐标值即可
void Bresenham(const unsigned& x1, const unsigned& y1, const unsigned& x2, const unsigned& 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 (unsigned y = y1; y <= y2; ++y)
{
putpixel(x1, y, YELLOW);
}
return;
}
//对于不同的变化方向以及斜率的正负不同,需要使用不同的数学表达式(自行推导出),也就是需要分情况
double k = double(dy) / dx;
if (Direction == x)//处理主要变化方向为x轴的情况
{
unsigned x = x1, y = y1;
if (k >= 0)//主要变化方向为x轴并且斜率大于等于零
{
int e = -dx;
for (; x <= x2; ++x)
{
putpixel(x, y, YELLOW);
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, YELLOW);
e += 2 * dy;
if (e <= 0)
{
y--;
e += 2 * dx;
}
}
}
}
else if (Direction == y)
{
unsigned y = y1, x = x1;
if (k >= 0)//主要变化方向为y轴方向并且斜率大于等于零
{
int e = -dy;
for (; y <= y2; ++y)
{
putpixel(x, y, YELLOW);
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, YELLOW);
e += 2 * dx;
if (e <= 0)
{
x--;
e += 2 * dy;
}
}
}
}
return;
}
//SH算法函数:需要输入多边形顶点的横坐标数组、纵坐标数组以及多边形的顶点个数
void SH_PolygonClip(const int* x, const int* y, const unsigned& num)
{
//定义两组向量,其中一组向量用于某一阶段的点结果保存,另一组向量用于辅助计算
vector<int>Xvec, Yvec;
vector<int>temp_Xvec, temp_Yvec;
//首先用原有数组中的元素对向量分别进行初始化
for (unsigned i = 0; i < num; i++)
{
Xvec.push_back(x[i]);
Yvec.push_back(y[i]);
}
//接下来分别用四条边界,按照上下左右的顺序对原多边形进行裁剪,每完成一条边界的裁剪便绘制一次裁剪结果
//首先进行上边界的裁剪
for (unsigned i = 0; i < Xvec.size() - 1; ++i)
{
//对于纵坐标小于上边界的点,将其保留在点集中
if (Yvec[i] <= wy2)
{
temp_Xvec.push_back(Xvec[i]);
temp_Yvec.push_back(Yvec[i]);
}
//如果多边形某一条边上的两点分别处在边界的两侧,则找出该直线与边界的交点,将交点放入向量中
if ((Yvec[i] - wy2) * (Yvec[i + 1] - wy2) < 0)
{
//首先考虑斜率不存在的情况
if (Xvec[i] == Xvec[i + 1])
{
temp_Xvec.push_back(Xvec[i]);
temp_Yvec.push_back(wy2);
}
//接着考虑斜率存在的情况,对于该情况,为了使得结果更加精确,对点的横坐标进行了四舍五入计算
else
{
int new_x = int(((double)(Xvec[i + 1] - Xvec[i]) / (Yvec[i + 1] - Yvec[i])) * (wy2 - Yvec[i]) + Xvec[i]+0.5);
temp_Xvec.push_back(new_x);
temp_Yvec.push_back(wy2);
}
}
}
//接下来处理最后一个顶点以及最后一个顶点和第一个顶点的连线,处理方法与上类似
if (Yvec[Xvec.size()-1] <= wy2)
{
temp_Xvec.push_back(Xvec[Xvec.size() - 1]);
temp_Yvec.push_back(Yvec[Xvec.size() - 1]);
}
if ((Yvec[Xvec.size() - 1] - wy2) * (Yvec[0] - wy2) < 0)
{
if (Xvec[Xvec.size() - 1] == Xvec[0])
{
temp_Xvec.push_back(Xvec[0]);
temp_Yvec.push_back(wy2);
}
else
{
int new_x = int(((double)(Xvec[Xvec.size() - 1] - Xvec[0]) / (Yvec[Yvec.size() - 1] - Yvec[0])) * (wy2 - Yvec[0]) + Xvec[0] + 0.5);
temp_Xvec.push_back(new_x);
temp_Yvec.push_back(wy2);
}
}
//将临时结果进行保存从而更新结果向量,同时清空临时向量便于下次重新使用
Xvec = temp_Xvec;
Yvec = temp_Yvec;
temp_Xvec.clear();
temp_Yvec.clear();
//绘制裁剪一次之后的效果,此处使用了之前所学习的Bresenham算法进行直线绘制
initgraph(1000, 640);
rectangle(wx1, wy2, wx2, wy1);
for (unsigned i = 0; i < Xvec.size() - 1; i++)
{
Bresenham(Xvec[i], Yvec[i], Xvec[i + 1], Yvec[i + 1]);
}
Bresenham(Xvec[0], Yvec[0], Xvec[Xvec.size() - 1], Yvec[Yvec.size() - 1]);
//使程序休眠一段时间,以方便用户看清运行结果
Sleep(2000);
closegraph();
//进行下边界的裁剪,内容与上边界的裁剪类似
for (unsigned i = 0; i < Xvec.size() - 1; ++i)
{
if (Yvec[i] >= wy1)
{
temp_Xvec.push_back(Xvec[i]);
temp_Yvec.push_back(Yvec[i]);
}
if ((Yvec[i] - wy1) * (Yvec[i + 1] - wy1) < 0)
{
if (Xvec[i] == Xvec[i + 1])
{
temp_Xvec.push_back(Xvec[i]);
temp_Yvec.push_back(wy1);
}
else
{
int new_x = int(((double)(Xvec[i + 1] - Xvec[i]) / (Yvec[i + 1] - Yvec[i])) * (wy1 - Yvec[i]) + Xvec[i] + 0.5);
temp_Xvec.push_back(new_x);
temp_Yvec.push_back(wy1);
}
}
}
if (Yvec[Xvec.size() - 1] >= wy1)
{
temp_Xvec.push_back(Xvec[Xvec.size() - 1]);
temp_Yvec.push_back(Yvec[Xvec.size() - 1]);
}
if ((Yvec[Xvec.size() - 1] - wy1) * (Yvec[0] - wy1) < 0)
{
if (Xvec[Xvec.size() - 1] == Xvec[0])
{
temp_Xvec.push_back(Xvec[0]);
temp_Yvec.push_back(wy1);
}
else
{
int new_x = int(((double)(Xvec[Xvec.size() - 1] - Xvec[0]) / (Yvec[Yvec.size() - 1] - Yvec[0])) * (wy1 - Yvec[0]) + Xvec[0] + 0.5);
temp_Xvec.push_back(new_x);
temp_Yvec.push_back(wy1);
}
}
Xvec = temp_Xvec;
Yvec = temp_Yvec;
temp_Xvec.clear();
temp_Yvec.clear();
initgraph(1000, 640);
rectangle(wx1, wy2, wx2, wy1);
for (unsigned i = 0; i < Xvec.size() - 1; i++)
{
Bresenham(Xvec[i], Yvec[i], Xvec[i + 1], Yvec[i + 1]);
}
Bresenham(Xvec[0], Yvec[0], Xvec[Xvec.size() - 1], Yvec[Yvec.size() - 1]);
Sleep(2000);
closegraph();
//进行左边界的裁剪,内容与上边界的裁剪类似
for (unsigned i = 0; i < Xvec.size() - 1; ++i)
{
if (Xvec[i] >= wx1)
{
temp_Xvec.push_back(Xvec[i]);
temp_Yvec.push_back(Yvec[i]);
}
if ((Xvec[i] - wx1) * (Xvec[i + 1] - wx1) < 0)
{
double k = (double)(Yvec[i + 1] - Yvec[i]) / (Xvec[i + 1] - Xvec[i]);
int new_y = int(k * (wx1 - Xvec[i]) + Yvec[i] + 0.5);
temp_Xvec.push_back(wx1);
temp_Yvec.push_back(new_y);
}
}
if (Xvec[Xvec.size()-1] >= wx1)
{
temp_Xvec.push_back(Xvec[Xvec.size()-1]);
temp_Yvec.push_back(Yvec[Xvec.size()-1]);
}
if ((Xvec[Xvec.size() - 1] - wx1) * (Xvec[0] - wx1) < 0)
{
double k = (double)(Yvec[Xvec.size() - 1] - Yvec[0]) / (Xvec[Xvec.size() - 1] - Xvec[0]);
int new_y = int(k * (wx1 - Xvec[0]) + Yvec[0] + 0.5);
temp_Xvec.push_back(wx1);
temp_Yvec.push_back(new_y);
}
Xvec = temp_Xvec;
Yvec = temp_Yvec;
temp_Xvec.clear();
temp_Yvec.clear();
initgraph(1000, 640);
rectangle(wx1, wy2, wx2, wy1);
for (unsigned i = 0; i < Xvec.size() - 1; i++)
{
Bresenham(Xvec[i], Yvec[i], Xvec[i + 1], Yvec[i + 1]);
}
Bresenham(Xvec[0], Yvec[0], Xvec[Xvec.size() - 1], Yvec[Yvec.size() - 1]);
Sleep(2000);
closegraph();
//进行右边界的裁剪,内容与上边界的裁剪类似
for (unsigned i = 0; i < Xvec.size() - 1; ++i)
{
if (Xvec[i] <= wx2)
{
temp_Xvec.push_back(Xvec[i]);
temp_Yvec.push_back(Yvec[i]);
}
if ((Xvec[i] - wx2) * (Xvec[i + 1] - wx2) < 0)
{
double k = (double)(Yvec[i + 1] - Yvec[i]) / (Xvec[i + 1] - Xvec[i]);
int new_y = int(k * (wx2 - Xvec[i]) + Yvec[i] + 0.5);
temp_Xvec.push_back(wx2);
temp_Yvec.push_back(new_y);
}
}
if (Xvec[Xvec.size() - 1] <= wx2)
{
temp_Xvec.push_back(Xvec[Xvec.size() - 1]);
temp_Yvec.push_back(Yvec[Xvec.size() - 1]);
}
if ((Xvec[Xvec.size() - 1] - wx2) * (Xvec[0] - wx2) < 0)
{
double k = (double)(Yvec[Xvec.size() - 1] - Yvec[0]) / (Xvec[Xvec.size() - 1] - Xvec[0]);
int new_y = int(k * (wx2 - Xvec[0]) + Yvec[0] + 0.5);
temp_Xvec.push_back(wx2);
temp_Yvec.push_back(new_y);
}
Xvec = temp_Xvec;
Yvec = temp_Yvec;
temp_Xvec.clear();
temp_Yvec.clear();
initgraph(1000, 640);
rectangle(wx1, wy2, wx2, wy1);
for (unsigned i = 0; i < Xvec.size()-1; i++)
{
Bresenham(Xvec[i],Yvec[i],Xvec[i+1],Yvec[i+1]);
}
Bresenham(Xvec[0], Yvec[0], Xvec[Xvec.size() - 1], Yvec[Yvec.size() - 1]);
_getch();
closegraph();
return;
}
int main(void)
{
unsigned PointNum;
cout << "请输入多边形的顶点数:";
cin >> PointNum;
int* xArray = new int[PointNum];
int* yArray = new int[PointNum];
cout << "请输入多边形每个点的横坐标:";
for (unsigned i = 0; i < PointNum; i++)
{
cin >> xArray[i];
}
cout << "请输入多边形每个点的纵坐标:";
for (unsigned i = 0; i < PointNum; i++)
{
cin >> yArray[i];
}
cout << "请输入窗口左下角点和右上角点的横纵坐标:";
cin >> wx1 >> wy1 >> wx2 >> wy2;
cout << "参数确认完毕,按任意键开始程序...(动态绘图完成后按任意键可返回)" << endl;
initgraph(1000, 640);
for (unsigned i = 0; i < PointNum-1; i++)
{
Bresenham(xArray[i], yArray[i], xArray[i + 1], yArray[i + 1]);
}
Bresenham(xArray[PointNum - 1], yArray[PointNum - 1], xArray[0], yArray[0]);
rectangle(wx1, wy2, wx2, wy1);
Sleep(2000);
closegraph();
SH_PolygonClip(xArray, yArray, PointNum);
return 0;
}
Suthurland-Hodgman多边形裁剪算法-C++完整实现(计算机图形学作业)
最新推荐文章于 2022-04-12 16:32:29 发布