opengl/c++ 矢量法切割凹多边形并填充(附代码)

        opengl的GL_POLYGON只能渲染凸多边形,如果想渲染凹多变性,已经有较为高效的方式。出于学习算法和锻炼写码能力,使用矢量法来切割凹多边形并填充。

        对于图类的问题,建立好图形表是解决问题的基础和关键。一开始多次写到一半写不下去了就是因为没用多边形表的惯例来层层分割。

        由于本人很菜,代码比较冗余。首先是数据结构,分别是点表,边表,和面表,以及相应的元素的定义,用vector实现。judge是表示线的参数。然后是函数声明。

struct V {
	int x, y;
};
struct E {
	int front, rear;
	V Vector;
};

struct judge {
	float a, b;
};


typedef	vector<int> Epos;

vector<V>pointlist;
vector<E>linelist;
vector<Epos>flatlist;
vector<bool>flatindex;


V vtemp;
E etemp;
Epos ftemp;
int pos;

bool is_not_exist_point;
int Vectormult(V&, V&);
bool conser(Epos&);
void cutrect();
int newconser(Epos&);
short check(judge&, V&);
bool nextcheck_i(V&, V&, V&);
bool finalcheck(V&, V&);
void Drawrect();

        主函数输入数据(要逆时针),int型点,最好不要相差太小(比如只差1之类)会因为用的int而不准确。在主函数中检测是否已经是凸多边形,如果是就直接画图,不是就进入cutrect()递归运算。cutrect()是对flatlist中的每个面片进行检测,如果每个都是凸多边形就停止递归,开始画图。

int main(int argc, char* argv[]) {
	
	int index = 0,whe=0;
	scanf_s("%d%d", &vtemp.x, &vtemp.y);
	pointlist.push_back(vtemp);
	while(scanf_s("%d%d",&vtemp.x,&vtemp.y)){
		pointlist.push_back(vtemp);
		etemp.front = index;
		etemp.Vector.x = vtemp.x - pointlist.at(index).x;
		etemp.Vector.y = vtemp.y - pointlist.at(index).y;
		etemp.rear = ++index;

		linelist.push_back(etemp);
	}
	etemp.front = index;
	etemp.rear = 0;
	etemp.Vector.x = pointlist.at(0).x - pointlist.at(index).x;
	etemp.Vector.y = pointlist.at(0).y - pointlist.at(index).y;
	linelist.push_back(etemp);
	for (int i = 0; i < linelist.size(); i++) {
		ftemp.push_back(i);
	}
	flatlist.push_back(ftemp);
	if (pointlist.size() < 3) {
		cout << "not a rect\n";
		whe = 1;
	}
	else if (conser(flatlist.at(0))) {
		cout << "已经是凸多边形了\n";
		flatindex.push_back(1);
		whe = 1;
	}
	if (whe == 0) {
		flatindex.push_back(0);
		cutrect();
	}

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);
	glutInitWindowPosition(10, 10);
	glutInitWindowSize(500, 500);
	glutCreateWindow("切割凹多边形");


	init();
	glutDisplayFunc(Drawrect);
	glutReshapeFunc(ChangeSize);
	glutMainLoop();

}

        如果已经是凹多边形,需要考虑以下问题:

一:找出凹进去的边,做出射线

二,遍历除了与这条线相连以外所有的边,找出最近的一个相交点。判断是否相交和最近可以用向量法来完成,可以节省计算量。

三:分析这个点是已经存在还是新的点,如果是新的要修改三条边,不是就是两条边,然后从原面片把新面的边剔除,把新面入flatlist。

四:持续cutrect()循环直到满足条件。贴部分代码如下

int newconser(Epos &flat) {
	//这个pos是内部位置,且满足逆时针
	judge start;
	int ans;
	E Vfront = linelist[flat[pos]];
	V Pfront = pointlist[Vfront.front];
	if (Vfront.Vector.x == 0)
		start.a = INT_MAX;
	else start.a = float(Vfront.Vector.y) / float(Vfront.Vector.x);
	if(start.a!=INT_MAX)
	start.b = float(Pfront.y) - start.a * float(Pfront.x);
	else start.b = Pfront.x;
	vtemp.x = Vfront.Vector.x >= 0 ? INT_MAX/2 : INT_MIN/2;
	vtemp.y = Vfront.Vector.y >= 0 ? INT_MAX/2 : INT_MIN/2;
	for (int i = 0; i < pos - 1; i++) {
		E Etemp = linelist[flat[i]];
		V pointv = pointlist[Etemp.front];
		V pointw = pointlist[Etemp.rear];
		V newpoint;
		int numv = check(start, pointv);
		int numw = check(start, pointw);
		if (numv*numw > 0)
			continue;
		if (numw == 0) 
			if (nextcheck_i(pointw, Pfront, Vfront.Vector)) 
				if(finalcheck(pointw, Vfront.Vector)) {
				vtemp = pointw;
				ans = i; is_not_exist_point = 0;
				i++;
			}
				else {
					i++; continue;
				}
			else {
				i++; continue;
			}
		else {
			judge temp;
			if (Etemp.Vector.x == 0) {
				temp.a = INT_MAX;
				temp.b = pointv.x;
			}
			else {
				temp.a = Etemp.Vector.y / Etemp.Vector.x;
				temp.b = float(pointv.y) - temp.a*(float)pointv.x;
			}
			if (temp.a != INT_MAX && start.a != INT_MAX) {
				float num = (start.b - temp.b) / (temp.a - start.a);
				newpoint.x = num;
				newpoint.y = start.a*num + start.b;
				if (nextcheck_i(newpoint, Pfront, Vfront.Vector) && finalcheck(newpoint, Vfront.Vector)) {
					vtemp = newpoint;
					ans = i;
					is_not_exist_point = 1;
				}
				else continue;
			}
			else if (temp.a == INT_MAX) {
				newpoint.x = temp.b;
				newpoint.y = start.a*temp.b + start.b;
				if (nextcheck_i(newpoint, Pfront, Vfront.Vector) && finalcheck(newpoint, Vfront.Vector)) {
					vtemp = newpoint;
					ans = i;
					is_not_exist_point = 1;
				}
				else continue;
			}
		}

	}
	for(int i=pos+2;i<flat.size();i++){
		E Etemp = linelist[flat[i]];
		V pointv = pointlist[Etemp.front];
		V pointw = pointlist[Etemp.rear];
		V newpoint;
		int numv = check(start, pointv);
		int numw = check(start, pointw);
		if (numv*numw > 0)
			continue;
		if (numw == 0)
			if (nextcheck_i(pointw, Pfront, Vfront.Vector))
				if (finalcheck(pointw, Vfront.Vector)) {
					vtemp = pointw;
					ans = i; is_not_exist_point = 0;
					i++;
				}
				else {
					i++; continue;
				}
			else {
				i++; continue;
			}
		else {
			judge temp;
			if (Etemp.Vector.x == 0) {
				temp.a = INT_MAX;
				temp.b = pointv.x;
			}
			else {
				temp.a = Etemp.Vector.y / Etemp.Vector.x;
				temp.b = float(pointv.y) - temp.a*(float)pointv.x;
			}
			if (temp.a != INT_MAX && start.a != INT_MAX) {
				float num = (start.b - temp.b) / (temp.a - start.a);
				newpoint.x = num;
				newpoint.y = start.a*num + start.b;
				if (nextcheck_i(newpoint, Pfront, Vfront.Vector) && finalcheck(newpoint, Vfront.Vector)) {
					vtemp = newpoint;
					ans = i;
					is_not_exist_point = 1;
				}
				else continue;
			}
			else if (temp.a == INT_MAX) {
				newpoint.x = temp.b;
				newpoint.y = start.a*temp.b + start.b;
				if (nextcheck_i(newpoint, Pfront, Vfront.Vector) && finalcheck(newpoint, Vfront.Vector)) {
					vtemp = newpoint;
					ans = i;
					is_not_exist_point = 1;
				}
				else continue;
			}
		}

	}
	return ans;
}

最终效果如下

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值