多边形裁剪算法

1.先决定窗口范围:

initgraph(800, 600);
	line(200, 450, 200, 200);
	line(200, 200, 600, 200);
	line(600, 200, 600, 450);
	line(600, 450, 200, 450);//框

2.进行鼠标操作:鼠标左键按下:画点;鼠标右键按下:连线;鼠标中键按下:与初始点相连接,完成多边形的绘制。

void shubiao() {
	int a[3] = { 0 }, b[3] = { 0 };
	int count = 0;
	int a1 = 0, b1 = 0;//初始点
	int flag = 0;
	cout << "鼠标左键按下:画点\n鼠标右键按下:连线\n鼠标中键按下:与初始点相连接,完成多边形的绘制\n" << endl;
	while (flag==0) {
		if (MouseHit())//判断是否有鼠标信息
		{
			settextcolor(RED);//默认为红色
			MOUSEMSG msg;  //定义一个鼠标消息
			msg = GetMouseMsg();//获取鼠标消息
			switch (msg.uMsg)
			{
			case  WM_LBUTTONDOWN:
			{
				putpixel(msg.x, msg.y, RED);//画点
				xx[count] = msg.x;
				yy[count] = msg.y;
				a[count % 2] = msg.x;
				b[count % 2] = msg.y;
				if (count == 0) {
					a1 = msg.x;
					b1 = msg.y;
				}
				count++;

				Vertex p(msg.x, msg.y);
				input_vertice.push_back(p);

				break;
			}// 左键按下消息 画点
			case  WM_RBUTTONDOWN:
			{
				setlinecolor(RED);
				line(a[0], b[0], a[1], b[1]);
				break;
			}// 右键按下消息 连线
			case   WM_MBUTTONDOWN:
			{
				setlinecolor(RED);
				line(a[(count - 1) % 2], b[(count - 1) % 2], a1, b1);
				cout << "已完成多边形的绘制" << endl;
				flag = 1;
				break;
			}// 中键按下消息 完成绘图
			}
		}
	}
}

最后与初始点连接时不要按右键,要按中键。

3.进行多边形裁剪:这里使用Sutherland-Hodgman算法。主要是Inside(判断点是否在裁剪边的可见侧),Intersect(直线段与窗口求交,返回交点),SutherlandHodgmanClip函数。


bool Inside(Vertex& testPT, EDGE ClipBoundary)
{//判断点是否在裁剪边的可见侧
	if (ClipBoundary.bx > ClipBoundary.ax)
	{
		if (testPT.y >= ClipBoundary.ay)//裁剪边为窗口下边
			return true;
	}
	else if (ClipBoundary.bx < ClipBoundary.ax)
	{
		if (testPT.y <= ClipBoundary.ay)//裁剪边在窗口上边
			return true;
	}
	else if (ClipBoundary.by > ClipBoundary.ay)//裁剪边在窗口右边
	{
		if (testPT.x <= ClipBoundary.ax)
			return true;
	}
	else if (ClipBoundary.by < ClipBoundary.ay)//裁剪边在窗口左边
	{
		if (testPT.x >= ClipBoundary.ax)
			return true;
	}
	return false;
}

void Intersect(Vertex& S, Vertex& P, EDGE ClipBoundary, Vertex& IntersectPt)
{
	if (ClipBoundary.ay == ClipBoundary.by)//水平裁剪
	{
		IntersectPt.y = ClipBoundary.ay;
		IntersectPt.x = S.x + (ClipBoundary.ay - S.y) * (P.x - S.x) / (P.y - S.y);
	}
	else//垂直裁剪
	{
		IntersectPt.x = ClipBoundary.ax;
		IntersectPt.y = S.y + (ClipBoundary.ax - S.x) * (P.y - S.y) / (P.x - S.x);
	}
}



void SutherlandHodgmanClip(EDGE ClipBoundary)
{
	Vertex S, P, ip;
	output_vertice.clear();
	S = input_vertice[input_vertice.size() - 1];//先从最后一个点指向第一个点的线段开始检验

	for (int j = 0; j < input_vertice.size(); j++)
	{
		P = input_vertice[j];
		if (Inside(P, ClipBoundary))//P在里面
		{
			if (Inside(S, ClipBoundary))//SP都在窗口内
			{
				output_vertice.push_back(P);
			}
			else//P在里面 S在外面
			{
				Intersect(S, P, ClipBoundary, ip);
				output_vertice.push_back(ip);
				output_vertice.push_back(P);
			}
		}
		else//P在外面
		{
			if (Inside(S, ClipBoundary))//S在窗口内P在窗口外
			{
				Intersect(S, P, ClipBoundary, ip);
				output_vertice.push_back(ip);
			}
			//SP都在外面则无输出
		}
		S = P;
	}
	input_vertice = output_vertice;//这次的输出作为下一次的输入
}

4.键盘或鼠标响应裁剪命令。这里我使用“w”键。

void  keyboard() {
	cout << "按w开始裁剪..." << endl;
	//settextcolor(BLUE);//填充蓝色
	int key = _getch();
	switch (key)
	{
		//w键
	case 72:
	case'w':
	case'W':
		SutherlandHodgmanClip(::left);
		SutherlandHodgmanClip(bottom);
		SutherlandHodgmanClip(::right);
		SutherlandHodgmanClip(top);
		for (int i = 0; i < output_vertice.size() - 1; i++)
		{
			putpixel(output_vertice[i].x, output_vertice[i].y, RED);
			line(output_vertice[i].x, output_vertice[i].y, output_vertice[i + 1].x, output_vertice[i + 1].y);
		}
		line(output_vertice[output_vertice.size() - 1].x, output_vertice[output_vertice.size() - 1].y, output_vertice[0].x, output_vertice[0].y);
		break;

	    default:cout << "输入有误!" << endl;
	}
}

5.主函数:

int main() {
	initgraph(800, 600);
	line(200, 450, 200, 200);
	line(200, 200, 600, 200);
	line(600, 200, 600, 450);
	line(600, 450, 200, 450);//框
	//多边形裁剪
	shubiao();
	setlinestyle(PS_SOLID, 5);
	keyboard();


	system("pause");
	closegraph();
	return 0;
}

6.最后结果:

7.最终代码:

#include <graphics.h>
#include <vector>
#include <algorithm>
#include<iostream>
#include<conio.h> 
using namespace std;
const int maxn = 50;
const int window_width = 800, window_height = 600;
int xx[maxn] = { 0 }, yy[maxn] = { 0 };//存各点坐标

struct Vertex {
	float x;
	float y;
	Vertex() {}
	Vertex(float xx, float yy)
		:x(xx), y(yy) {}
	bool operator < (const Vertex& a)const //重载运算符
	{
		return x < a.x;
	}
};
typedef Vertex edge[2];
typedef Vertex vertexArray[maxn];

struct EDGE//Edge边
{
	float ax, ay, bx, by;
	EDGE() {}
	EDGE(float bxx, float byy, float exx, float eyy)
		:ax(bxx), ay(byy), bx(exx), by(eyy) {}
};

vector<Vertex> input_vertice; //输入
vector<Vertex> output_vertice; //输出

EDGE left(200, 450, 200, 200);
EDGE bottom(200, 200, 600, 200);
EDGE right(600, 200, 600, 450);
EDGE top(600, 450, 200, 450);


bool Inside(Vertex& testPT, EDGE ClipBoundary)
{//判断点是否在裁剪边的可见侧
	if (ClipBoundary.bx > ClipBoundary.ax)
	{
		if (testPT.y >= ClipBoundary.ay)//裁剪边为窗口下边
			return true;
	}
	else if (ClipBoundary.bx < ClipBoundary.ax)
	{
		if (testPT.y <= ClipBoundary.ay)//裁剪边在窗口上边
			return true;
	}
	else if (ClipBoundary.by > ClipBoundary.ay)//裁剪边在窗口右边
	{
		if (testPT.x <= ClipBoundary.ax)
			return true;
	}
	else if (ClipBoundary.by < ClipBoundary.ay)//裁剪边在窗口左边
	{
		if (testPT.x >= ClipBoundary.ax)
			return true;
	}
	return false;
}

void Intersect(Vertex& S, Vertex& P, EDGE ClipBoundary, Vertex& IntersectPt)
{
	if (ClipBoundary.ay == ClipBoundary.by)//水平裁剪
	{
		IntersectPt.y = ClipBoundary.ay;
		IntersectPt.x = S.x + (ClipBoundary.ay - S.y) * (P.x - S.x) / (P.y - S.y);
	}
	else//垂直裁剪
	{
		IntersectPt.x = ClipBoundary.ax;
		IntersectPt.y = S.y + (ClipBoundary.ax - S.x) * (P.y - S.y) / (P.x - S.x);
	}
}



void SutherlandHodgmanClip(EDGE ClipBoundary)
{
	Vertex S, P, ip;
	output_vertice.clear();
	S = input_vertice[input_vertice.size() - 1];//先从最后一个点指向第一个点的线段开始检验

	for (int j = 0; j < input_vertice.size(); j++)
	{
		P = input_vertice[j];
		if (Inside(P, ClipBoundary))//P在里面
		{
			if (Inside(S, ClipBoundary))//SP都在窗口内
			{
				output_vertice.push_back(P);
			}
			else//P在里面 S在外面
			{
				Intersect(S, P, ClipBoundary, ip);
				output_vertice.push_back(ip);
				output_vertice.push_back(P);
			}
		}
		else//P在外面
		{
			if (Inside(S, ClipBoundary))//S在窗口内P在窗口外
			{
				Intersect(S, P, ClipBoundary, ip);
				output_vertice.push_back(ip);
			}
			//SP都在外面则无输出
		}
		S = P;
	}
	input_vertice = output_vertice;//这次的输出作为下一次的输入
}


void shubiao() {
	int a[3] = { 0 }, b[3] = { 0 };
	int count = 0;
	int a1 = 0, b1 = 0;//初始点
	int flag = 0;
	cout << "鼠标左键按下:画点\n鼠标右键按下:连线\n鼠标中键按下:与初始点相连接,完成多边形的绘制\n" << endl;
	while (flag == 0) {
		if (MouseHit())//判断是否有鼠标信息
		{
			settextcolor(RED);//默认为红色
			MOUSEMSG msg;  //定义一个鼠标消息
			msg = GetMouseMsg();//获取鼠标消息
			switch (msg.uMsg)
			{
			case  WM_LBUTTONDOWN:
			{
				putpixel(msg.x, msg.y, RED);//画点
				xx[count] = msg.x;
				yy[count] = msg.y;
				a[count % 2] = msg.x;
				b[count % 2] = msg.y;
				if (count == 0) {
					a1 = msg.x;
					b1 = msg.y;
				}
				count++;

				Vertex P(msg.x, msg.y);
				input_vertice.push_back(P);

				break;
			}// 左键按下消息 画点
			case  WM_RBUTTONDOWN:
			{
				setlinecolor(RED);
				line(a[0], b[0], a[1], b[1]);
				break;
			}// 右键按下消息 连线
			case   WM_MBUTTONDOWN:
			{
				setlinecolor(RED);
				line(a[(count - 1) % 2], b[(count - 1) % 2], a1, b1);
				cout << "已完成多边形的绘制" << endl;
				flag = 1;
				break;
			}// 中键按下消息 完成绘图
			}
		}
	}
}
void  keyboard() {
	cout << "按w开始裁剪..." << endl;
	//settextcolor(BLUE);//填充蓝色
	int key = _getch();
	switch (key)
	{
		//w键
	case 72:
	case'w':
	case'W':
		SutherlandHodgmanClip(::left);
		SutherlandHodgmanClip(bottom);
		SutherlandHodgmanClip(::right);
		SutherlandHodgmanClip(top);
		for (int i = 0; i < output_vertice.size() - 1; i++)
		{
			putpixel(output_vertice[i].x, output_vertice[i].y, RED);
			line(output_vertice[i].x, output_vertice[i].y, output_vertice[i + 1].x, output_vertice[i + 1].y);
		}
		line(output_vertice[output_vertice.size() - 1].x, output_vertice[output_vertice.size() - 1].y, output_vertice[0].x, output_vertice[0].y);
		break;

	    default:cout << "输入有误!" << endl;
	}
}


int main() {
	initgraph(800, 600);
	line(200, 450, 200, 200);
	line(200, 200, 600, 200);
	line(600, 200, 600, 450);
	line(600, 450, 200, 450);//框
	//多边形裁剪
	shubiao();
	setlinestyle(PS_SOLID, 5);
	keyboard();


	system("pause");
	closegraph();
	return 0;
}

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值