图分割,计算几何

来源:互联网 发布:webpack生成JS 编辑:程序博客网 时间:2024/04/28 22:39

 

在计算几何中,对于基本的操作,如图形求并,求差,求与等,都已有很成熟的算法可以摆平了..
但在实际应用中,还有一种情况是研究得较少的:
       给出条件:一个图形,用一条线(此线首尾不相联)去剪 ,形象点的比喻为:用剪去剪一个图形,这条线的轨迹为剪刀的路径.
       结果:将图形分成一片一片:但要求原面积不能变噢.
       图形允许形式,单环,多环状,环中带洞.
       刚开始时,还以为挺简单的,用一两天总能搞定,实际做的时候,发现这情况并不是想象中的这么简单.用了近一个星期的时间,洋洋洒洒的也有二千行代码,才搞定..成绩单在下面
      另外,因为过程还是有点复杂,而且,还有一种可能性出现导致有刚好套上洞后本应再分裂的图,没有作处理,,以后很有空时,再处理,所以留下思路,好提醒下.
      有兴趣的同学们可以按以下方式和作者联系讨论: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) ;    //两相段是否相交,共线,端点在线上,都为truebool dzq_intersect(dzq_lineseg u,dzq_lineseg v,dzq_pt *s ) ;//两相段是否相交,并求出求点,共线false,非共线没交点false,有交点或就是端点也为trueint  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,没有出剪则返回0int  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转到shapedzq_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 ;//第一个点的Xplt.y = line_hole[h].vertex[i].y ;//第一个点的Yfor (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;}