朋友给我的一个题目,让我写写看。
具体的题目可以见代码后附,这里简单描述一下。
就跟PS里可以用画多边形来抠曲线图形一样,三维的任意形状的物体也可以用很多个三维空间的三角形来拟合,只要拟合的单位足够多,最后的效果会不错。
用拟合的图形代替原图形,再用一个给定的平面去截,截到三角形的边上时会得到很多交点,全部连起来就可以得到截面图形。
给出一系列三角形,再输入一个平面,求该平面与每个三角形每条边的交点。如果无交点或者交点不在线段上,都算作没有。
至于重合的情况,暂时算作不计,但是后期要修改的话依然可以改。
输入格式
第一行:点的数量
第二行开始:点的 X Y Z坐标
点的坐标最后一行之后:三角形的数量
下一行开始:三角形三个顶点的编号,编号按照上面的顺序(从零开始计数)
输出格式
他没跟我讲,我就随便输出了。
根据线段和平面的关系,有四种输出。
相交->交点坐标
平行但不包含->Paraell
包含->Included
相交但交点不在线段上->Exceed
编辑器为UTF-8格式,如果用devcpp等打开乱码,请先用记事本打开后,存储为ANSI。
#include <stdio.h>
typedef struct _coor
{
double x,y,z;
}Coor;//单个点坐标
typedef Coor Vector;//向量和点的存储形式是一样的,但是还是区分一下
typedef struct _triangle
{
int pid1,pid2,pid3;
}Triangle;//由于单个点已经存入了,为了减少存储负担,这里只存储点的序号
Coor* cList;int coorCnt;
//点的数组,由于输入数据可能非常大,采用手动在堆里申请的策略防止爆栈
Triangle* tList;int triangleCnt;
//三角形的数组
double A,B,C,D;
//最后输入的平面的参数
Vector normalVec;
//平面的法向量
int isCommandOutput=1;//命令行输出开关
int isFileOutput=1;//文件输出开关
double innerProduct(Vector a,Vector b);
//计算两向量的内积
Vector calVector(Coor from,Coor to);
//返回从from指向to的向量
void TSintersect(Triangle t);
//Triangle and Surface Intersect
//处理单个三角形
int LSintersect(Coor p1,Coor p2,Coor *r);
//Line and Surface Intersect
//处理单条线段
int isBetween(double n1,double n2,double candi);
//检测数candi是否在n1和n2之间,配合isOnTheLine使用来检测交点是否在线段上
int isOnTheLine(Coor p1,Coor p2,Coor c);
//检测点c是否在线段p1p2上
int main(int argc, char const *argv[])
{
//测试数据从当前目录的Res01.txt读入
//结果将同时在命令行和文件output.txt输出,可以自行修改
FILE *f=fopen("Res01.txt","r");int i;
if(!f) {
printf("File cannot open!Program will exit.\n");
return 0;
}
//将点和三角形读入动态数组
fscanf(f,"%d",&coorCnt);
cList=(Coor*)malloc(sizeof(Coor)*coorCnt);
for(i=0;i<coorCnt;++i)
fscanf(f,"%lf%lf%lf",&cList[i].x,&cList[i].y,&cList[i].z);
fscanf(f,"%d",&triangleCnt);
tList=(Triangle*)malloc(sizeof(Triangle)*triangleCnt);
for(i=0;i<coorCnt;++i)
fscanf(f,"%d%d%d",&tList[i].pid1,&tList[i].pid2,&tList[i].pid3);
fclose(f);
//读入给定的平面
printf("Key in the formula of a surface, that is, key in A,B,C,D of Ax+by+Cz+D=0\n");
scanf("%lf%lf%lf%lf",&A,&B,&C,&D);
normalVec.x=A;normalVec.y=B;normalVec.z=C;
//处理每个三角形,策略在TSintersect内
for(i=0;i<triangleCnt;++i)
TSintersect(tList[i]);
free(cList);
free(tList);
return 0;
}
double innerProduct(Vector a,Vector b)
//计算向量内积
{
return a.x*b.x+a.y*b.y+a.z*b.z;
}
Vector calVector(Coor from,Coor to)
//输入两点,计算由from到to的向量
{
Vector v;
v.x=to.x-from.x;
v.y=to.y-from.y;
v.z=to.z-from.z;
return v;
}
enum LS_RELATIONSHIP{
INTERSECT,PARALLEL,INCLUDE,EXCEED
};//线段和平面的关系,依次为相交、平行、包含、交点不在线段上
//只有INTERSECT是有效的
void TSintersect(Triangle t)
//Triangle and Surface Intersect
//处理每个三角形和平面的关系
{
static triangle_id;
//给三角形编号
int times=0;
Coor p1,p2,r;
FILE *f;
if(isFileOutput) f=fopen("output.txt","a+");
if(isCommandOutput) printf("Triangle %d: ",triangle_id );
if(isFileOutput) fprintf(f,"Triangle %d: ",triangle_id );
while(times<3)
{
//三边轮换
switch(times)
{
case 0:p1=cList[t.pid1],p2=cList[t.pid2];break;
case 1:p1=cList[t.pid1],p2=cList[t.pid3];break;
case 2:p1=cList[t.pid2],p2=cList[t.pid3];break; }
switch(LSintersect(p1,p2,&r))
{
case INTERSECT:
if(isCommandOutput) printf("(%lf,%lf,%lf) ",r.x,r.y,r.z );
if(isFileOutput) fprintf(f,"(%lf,%lf,%lf) ",r.x,r.y,r.z );
break;
case PARALLEL:
if(isCommandOutput) printf("Parallel but not included ");
if(isFileOutput) fprintf(f,"Parallel but not included " );
break;
case INCLUDE:
if(isCommandOutput) printf("Included ");
if(isFileOutput) fprintf(f, "Included ");
break;
case EXCEED:
if(isCommandOutput) printf("Cross but exceed the line ");
if(isFileOutput) fprintf(f, "Cross but exceed the line " );
break;
}
times++;
}
if(isCommandOutput) putchar('\n');
if(isFileOutput) fputc('\n',f);
triangle_id++;
if(isFileOutput) fclose(f);
}
int LSintersect(Coor p1,Coor p2,Coor *r)
//Line and Surface Intersect
//计算由p1和p2确定的线段与平面的交点
//int返回状态代码,交点由r返回
{
//方向向量与法向量垂直,直线平行平面,无交点
Vector p1p2=calVector(p1,p2);
double p1v=innerProduct(p1,normalVec)+D;//p1到平面的距离,省略掉了分母
double p2v=innerProduct(p2,normalVec)+D;//p2到平面的距离,省略掉了分母
if(p1v==p2v)//如果两点到平面的距离相等,那么为平行或者包含
{
if(p1v==0)//包含
return INCLUDE;
return PARALLEL;//平行,也没有交点
}
if(p2v==0)//如果p2在平面上,由于n要除以p2v,这里是要单独考虑的相交
{
*r=p2;
return INTERSECT;
}
double n=p1v/p2v;
double ratio=1+1/(n-1);
r->x=ratio*p1p2.x;;
r->y=ratio*p1p2.y;
r->z=ratio*p1p2.z;
//r此时为平面与直线的交点
if(isOnTheLine(p1,p2,*r))//如果在线段上
return INTERSECT;
return EXCEED;//不在线段上也不算
}
int isOnTheLine(Coor p1,Coor p2,Coor c)
{
if(isBetween(p1.x,p2.x,c.x) &&
isBetween(p1.y,p2.y,c.y) &&
isBetween(p1.z,p2.z,c.z))
return 1;
return 0;
}
int isBetween(double n1,double n2,double candi)
{
if( (n1<=candi&&candi<=n2) || (n2<=candi&&candi<=n1))
return 1;
return 0;
}