在计算几何中,对于基本的操作,如图形求并,求差,求与等,都已有很成熟的算法可以摆平了..
但在实际应用中,还有一种情况是研究得较少的:
给出条件:一个图形,用一条线(此线首尾不相联)去剪 ,形象点的比喻为:用剪去剪一个图形,这条线的轨迹为剪刀的路径.
结果:将图形分成一片一片:但要求原面积不能变噢.
图形允许形式,单环,多环状,环中带洞.
刚开始时,还以为挺简单的,用一两天总能搞定,实际做的时候,发现这情况并不是想象中的这么简单.用了近一个星期的时间,洋洋洒洒的也有二千行代码,才搞定..成绩单在下面
另外,因为过程还是有点复杂,而且,还有一种可能性出现导致有刚好套上洞后本应再分裂的图,没有作处理,,以后很有空时,再处理,所以留下思路,好提醒下.
有兴趣的同学们可以按以下方式和作者联系讨论: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) //结果的链表
//这个函数有很微小BUG,主要的原因是因为对图形去除微小的重合线,这样会产生面积减小,但都可控在1e-10的范围内.
//还有一种情况就是产生的结果环中的洞,则好将自己挖成两条线的情况,没有产生分裂.(本来处理好了,但需要多次递归,当裁剪刀痕达到30刀以上,十分耗内存,因此没有处理这种情况的分裂多图.)
//如果正确,则返回1,不正确返回-1,没有出剪则返回0
对每条外环进行循环.
用线去与环产生结果:dzq_generate_polygon 函数处理.
1,环,分成两部分.对其分裂保存起来ppl,没产生新的,则记下本身.
2,内环(洞) ppc所有一起加起来
2.1 内环与线产生洞凸出的部份ppv
产生另一部分分裂部分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,递归去产生图形
bool dzq_splitpolygon(dzq_polygon *p,dzq_polygon *pc ,dzq_polygon_list *result_polygon,int irecursion)
{//分裂原来的图形
//p 为原图,其本身可能有洞
//pc为洞参数
//result_polygon为结果链表
//irecursion 递归次数
取出自己本身的洞线
将带入的所有洞都加进来
//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;
}