Suthurland-Hodgman多边形裁剪算法-C++完整实现(计算机图形学作业)

#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值