图分割,计算几何

 

在计算几何中,对于基本的操作,如图形求并,求差,求与等,都已有很成熟的算法可以摆平了..
但在实际应用中,还有一种情况是研究得较少的:
       给出条件:一个图形,用一条线(此线首尾不相联)去剪 ,形象点的比喻为:用剪去剪一个图形,这条线的轨迹为剪刀的路径.
       结果:将图形分成一片一片:但要求原面积不能变噢.
       图形允许形式,单环,多环状,环中带洞.
       刚开始时,还以为挺简单的,用一两天总能搞定,实际做的时候,发现这情况并不是想象中的这么简单.用了近一个星期的时间,洋洋洒洒的也有二千行代码,才搞定..成绩单在下面
      另外,因为过程还是有点复杂,而且,还有一种可能性出现导致有刚好套上洞后本应再分裂的图,没有作处理,,以后很有空时,再处理,所以留下思路,好提醒下.
      有兴趣的同学们可以按以下方式和作者联系讨论:QQ:9492175 E-MAIL:DZQ168@21cn.com
       2013.3.24
先看看做出来的效果图:

 

 

 

整体思路:

int dzq_polygon_clip(dzq_polygon *subject_polygon,  //带入的多边形   
       dzq_vertex_list *vertex_lis,  //带入的线,线为非封闭
       dzq_polygon_list *result_polygon)         //结果的链表

        //注:result_polygon结果的链表,应在调用前生成一个对象带入来。
//这个函数有很微小BUG,主要的原因是因为对图形去除微小的重合线,这样会产生面积减小,但都可控在1e-10的范围内.
//还有一种情况就是产生的结果环中的洞,则好将自己挖成两条线的情况,没有产生分裂.(本来处理好了,但需要多次递归,当裁剪刀痕达到30刀以上,十分耗内存,因此没有处理这种情况的分裂多图.)
//如果正确,则返回1,不正确返回-1,没有出剪则返回0
主要步骤:
        对每条外环进行循环.
  用线去与环产生结果:dzq_generate_polygon 函数处理.
        1,环,分成两部分.对其分裂保存起来ppl,没产生新的,则记下本身.
        2,内环(洞) ppc所有一起加起来
        2.1 内环与线产生洞凸出的部份ppv
        产生大部数据据保存在dzq_polygon_clip(dzq_DIFF,ppl,ppc,pl1) pl1中.
        产生另一部分分裂部分dzq_polygon_clip(dzq_DIFF,subject_polygon,pl1,pl2); pl2中
        产生dzq_polygon_clip(dzq_INT,subject_polygon,ppv,pl3);  //求洞凸交集pl3中

 


        如果没有数据产生,就退出
        有剪出内容:
        先pl1与洞凸pl2求交集.dzq_polygon_clip(dzq_INT,pl1,pl3,pl4);如果有结果,则分成两部份去处理.
再依次调用dzq_eliminatesmicropolygon去处理微小删除部分完成部分后.
再调用dzq_splitpolygon,递归去产生图形
另一部和上面一样的办法去处理.
dzq_splitpolygon递归函数说明:
bool dzq_splitpolygon(dzq_polygon *p,dzq_polygon *pc ,dzq_polygon_list *result_polygon,int irecursion)
{//分裂原来的图形
//p 为原图,其本身可能有洞
//pc为洞参数
//result_polygon为结果链表
//irecursion 递归次数
        首先用dzq_splitpointinline去处理自身的分裂 去查看是否有点在自己本环的线上,有则分裂成两条线

取出自己本身的洞线
将带入的所有洞都加进来

 

//C 头文件
bool dzq_intersect(dzq_lineseg u,dzq_lineseg v) ;	    //两相段是否相交,共线,端点在线上,都为true
bool dzq_intersect(dzq_lineseg u,dzq_lineseg v,dzq_pt *s ) ;//两相段是否相交,并求出求点,共线false,非共线没交点false,有交点或就是端点也为true
int  dzq_ptinpolygon(dzq_vertex *vertex, dzq_vertex_list *vertex_lis);	//判断点是否在多边形内
bool dzq_polygon_SingleIntersect(dzq_polygon  *clip_polygon);		//单一图形是否有自相交的线
bool dzq_polygon_SingleIntersect(dzq_vertex_list *vertex_lis);		//单一图形是否有自相交的线
bool dzq_polygon_intersect(dzq_polygon  *clip_polygon);			//多图图形是否有自相交的线
bool dzq_generate_polygon(dzq_vertex_list *polygon_lis,			//面的线
						  dzq_vertex_list *line_lis,			//线
						  dzq_vertex_list *result_lis,			//结果
						  int iinoutin=-1)	;					//交点在什么位置 -1,在线上 0,在外,1在内	
//用线段去剪图形,这个线段为不闭合的线.
//如果正确,则返回1,不正确返回-1,没有出剪则返回0
int  dzq_polygon_clip(dzq_polygon *subject_polygon,		//带入的多边形				
					  dzq_vertex_list *vertex_lis,		//带入的线,线可为非封闭 					  int *num_polygon,					//生成的数量
					  dzq_polygon_list *result_polygon);//结果的链表,
bool dzq_splitpointinline(dzq_polygon *p);																				//点在别的线上,分裂出多个图
bool dzq_splitpolygon(dzq_polygon *p,dzq_polygon *pc ,dzq_polygon_list *result_polygon,int irecursion=0);				//分裂原来的图形
bool dzq_eliminatesmicropoint(dzq_vertex_list *vertex_lis);	//除去共线现象,即产生微线的三角图形,对于每条线来
bool dzq_eliminatesmicropolygon(dzq_polygon *p);			//除去一个对象内的不构成三角形的线.
dzq_polygon *dzq_vertesconvpolygon(dzq_vertex_list *v);		//由线产生一个简单单线面


double dzq_polygon_Area(dzq_vertex_list *vertex_lis);					//计算单一图形面积
double dzq_polygon_Area(dzq_polygon  *clip_polygon);					//计算图形的面积

SHPObject* dzq_conveyShape(dzq_polygon *clip_polygon);					//由面dzq转到shape
dzq_polygon* dzq_conveyClipPolygon(SHPObject* psObject);				//由面shape转到dzq


 

 


bool dzq_splitholeinline(dzq_polygon *p)
{	//这是洞可能在线上的情况
	//这里只有一种可能就是洞的顶点在线的上面.那么可能将其连接起来.
	//此函数有BUG,处理中空的情况,还是没有比较好的办法
	int c,h,v,i,j;
	double area_abc;
	double dbValve=1.1e-10;			//这是阀值,如果面积小于这个数,
	int iholes;//洞的数量
	iholes=0;
	if (p->num_contours<2) return false;	//只有一条线,没什么可以搞的.
	for (c=0;c<p->num_contours ;c++)
	{
		if (0 != p->hole[c]) iholes++;
	}
	if (iholes<1) return false;					//没有洞,不需要搞,这句话怎么这么有意义呢,难道是真理!
	//将所有洞线提取出来.
	dzq_vertex_list *line_hole	= (dzq_vertex_list*)malloc(iholes * sizeof(dzq_vertex_list));	//用于装中间生成结果的线
	dzq_vertex *hole_line;
	iholes=0;
	for (c=0;c<p->num_contours ;c++)
	{
		if (0 != p->hole[c])
		{
			line_hole[iholes].num_vertices = p->contour[c].num_vertices ;
			hole_line=(dzq_vertex*)malloc(line_hole[iholes].num_vertices * sizeof(dzq_vertex));
			line_hole[iholes].vertex = hole_line;
			for (i=0;i<line_hole[iholes].num_vertices;i++)
			{
				hole_line[i].x = p->contour[c].vertex[i].x ;
				hole_line[i].y = p->contour[c].vertex[i].y ;
			}
			iholes++;//洞的数量
		}
	}
	//然后将每条线与每个洞去处理看是否有顶点在外环上面
	dzq_pt plt,pls,ple; //三个点
	int num_vertices;	//线的顶点数
	for (c=0;c<p->num_contours ;c++)
	{
		if (0 != p->hole[c]) continue;				//是洞,不作处理.
		num_vertices=p->contour[c].num_vertices;	//线的顶点数
		for (h=0;h<iholes;h++)						//每个洞环
		{
			//确保所有洞点在环内
			bool bpinline=false;
			for (i=0;i<line_hole[h].num_vertices ;i++)	//洞的每个顶点
			{
				if (0==dzq_ptinpolygon(line_hole[h].vertex+i,p->contour +c))
				{ //点在外环的外面
					bpinline=true;
					break;
				}
			}
			if (true==bpinline) continue;
			//这个洞是否已更改过此外环?
			bool bHoleRingChange=false;	//这个环是否是已被此洞改了一次?

			//所有的洞点都在线的里面,可以继续了。
			for (i=0;i<line_hole[h].num_vertices ;i++)	//洞的每个顶点
			{
				plt.x = line_hole[h].vertex[i].x ;	//第一个点的X
				plt.y = line_hole[h].vertex[i].y ;	//第一个点的Y
				for (v=0;v<num_vertices;v++)	    //外环每条线的每线段
				{
					pls.x = p->contour[c].vertex[v].x ;	pls.y = p->contour[c].vertex[v].y;
					ple.x = p->contour[c].vertex[(v+1)%num_vertices].x ;	ple.y = p->contour[c].vertex[(v+1)%num_vertices].y;
					area_abc =  (pls.x -  ple.x ) * (plt.y - ple.y) - (pls.y -  ple.y ) * (plt.x - ple.x);
					area_abc = fabs(area_abc);
					if (area_abc<=dbValve															//三角形的面积二倍小于一个值
						&& (EQD(plt.x , min(pls.x , ple.x )) && EQX(plt.x ,max(pls.x , ple.x )) )	//X也其内
						&& (EQD(plt.y , min(pls.y , ple.y )) && EQX(plt.y ,max(pls.y , ple.y )) ))	//Y在其内
					{	//顶点在这条线上了.
						//产生新线来判断
						//拷贝线1外环的
						dzq_vertex_list *line_1	= (dzq_vertex_list*)malloc(1 * sizeof(dzq_vertex_list));	//用于装中间生成结果的线
						dzq_vertex_list *line_2	= (dzq_vertex_list*)malloc(1 * sizeof(dzq_vertex_list));	//用于装中间生成结果的线
						dzq_vertex *line1_vertex,*line2_vertex;
						line_1->num_vertices = num_vertices;
						line1_vertex=(dzq_vertex*)malloc(line_1->num_vertices * sizeof(dzq_vertex));
						line_1->vertex = line1_vertex;
						int ilinenew_num=0;	//新线的点当前偏移量
						for (j=v+1;j<num_vertices+v+1;j++)	//因为是外环,是从后一点开始的
						{
							line1_vertex[ilinenew_num].x = p->contour[c].vertex[j%num_vertices].x;
							line1_vertex[ilinenew_num].y = p->contour[c].vertex[j%num_vertices].y;
							ilinenew_num++;
						}
						
						//拷贝出线2洞的
						int h_num_vertices=line_hole[h].num_vertices;	//洞的顶点数
						line_2->num_vertices = h_num_vertices;
						line2_vertex=(dzq_vertex*)malloc(line_2->num_vertices * sizeof(dzq_vertex));
						line_2->vertex = line2_vertex;
						ilinenew_num=0;
						for (j=i;j< h_num_vertices + i ;j++) //就是从i这点开始的0
						{
							line2_vertex[ilinenew_num].x = line_hole[h].vertex[j%h_num_vertices].x;
							line2_vertex[ilinenew_num].y = line_hole[h].vertex[j%h_num_vertices].y;
							ilinenew_num++;
						}
						//之后再对这两条线进行处理
						//这两条线必须是非同向的,如果同向,则是打结的交叉线.,上面已有代码保证洞是在环的里面。
						double dbArealine1 = dzq_polygon_Area(line_1);	//计算方向
						double dbArealine2 = dzq_polygon_Area(line_2);	//计算方向
						//构造一条新线
						dzq_vertex_list *line_new	= (dzq_vertex_list*)malloc(1 * sizeof(dzq_vertex_list));	//用于装中间生成结果的线
						line_new->num_vertices = line_1->num_vertices + line_2->num_vertices + 1;				//比两者之和多出一个点
						dzq_vertex *linenew_vertex =(dzq_vertex*)malloc(line_new->num_vertices * sizeof(dzq_vertex));//装具体的点
						line_new->vertex = linenew_vertex;
						//先加入外环的线
						ilinenew_num=0;
						for (j=0;j<line_1->num_vertices;j++)
						{
							linenew_vertex[j].x =line_1->vertex[j].x;
							linenew_vertex[j].y =line_1->vertex[j].y;
							
						}
						ilinenew_num = line_1->num_vertices; //这时真正要用的偏移量值
						//这两条线必须是非同向的,如果同向,则是打结的交叉线.
						if (dbArealine1 * dbArealine2 >=0) //同向
						{	//因为同向,所以要将洞的点逆序加进来
							//0号点为交点,先加入一个自己的点
							linenew_vertex[ilinenew_num].x =line_2->vertex[0].x;
							linenew_vertex[ilinenew_num].y =line_2->vertex[0].y;
							ilinenew_num++;
							//倒过来加入其它的点
							for(j=line_2->num_vertices -1;j>=0;j--)
							{
								linenew_vertex[ilinenew_num].x =line_2->vertex[j].x;
								linenew_vertex[ilinenew_num].y =line_2->vertex[j].y;
								ilinenew_num++;
							}
						}
						else //已为非同向
						{
							for (j=0;j<line_2->num_vertices;j++)
							{
								linenew_vertex[ilinenew_num].x =line_2->vertex[j].x;
								linenew_vertex[ilinenew_num].y =line_2->vertex[j].y;
								ilinenew_num++;
							}
							//将最后一个点加入
							linenew_vertex[ilinenew_num].x =line_2->vertex[0].x;
							linenew_vertex[ilinenew_num].y =line_2->vertex[0].y;
							ilinenew_num++;
						}
						//先释放line_1;line_2因为已用完了				
						FREE(line_1->vertex); FREE( line_1);
						FREE(line_2->vertex); FREE( line_2);
						//看看是点值一样了
						//if (ilinenew_num != line_new->num_vertices)
						//{//可删除的IF,只是作个判断验证用。
						//	int jjjjjj=0;
						//}
						//更新线
						//删除原来的线
						FREE(p->contour[c].vertex );
						p->contour[c].num_vertices = line_new->num_vertices;
						p->contour[c].vertex = linenew_vertex;	//这个顶点数据linenew_vertex不能删除了
						FREE(line_new);
						//退出这个c环,到下下环
						bHoleRingChange=true;	//这个环是否是已被此洞改了一次?
						break;	//出到每个线段中去了

					}//if				
				} //for v外环每条线的每线段
				if (true==bHoleRingChange)	break; //退出到下一个洞去处理
			}//for i洞的每个顶点
		}//for h 洞
	}//for c外环

	//释放内存
	for (i=0;i<iholes;i++)
	{
		FREE(line_hole[i].vertex); 
	}
	FREE(line_hole);
	return false;	

}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值