(写得丑不要吐槽,毕竟我是蒟蒻)
题目大意:
给出n个球(n<=50)的坐标和半径,与一条光线起点和线
上一点(其实相减就可以得到射线方向了),光线在球面上遵循光的反射定律,要求你求出光线依次的反射对象(即经过哪些球),如果反射次数大于10,那么在输出反射10次后输出"etc.",中间用空格隔开。
解法分析:
显然对于一条光线和一个球,可以用光线的向量u和球的解析式求出交点(即找出(x,y,z)满足光线的解析式和球的解析式),因为方向向量f已知,所以可以解出t使得f*t+u(t>0,不然光线是反向的) 终点在球面上,所以这时你可以用交点和球的球心得到法线向量,然后求出反射向量就可以了。(对于每个球的考虑和第一个触碰到的球是哪个的判断请自己想)
但是最重要的就是如何通过入射向量和法线向量求出反射向量.
首先将入射向量反向,然后求出入射向量在法线向量上的投影,然后将法线向量加上投影与反向后的入射向量的差,就可以了
如何求投影请参考网友博客:http://www.cnblogs.com/graphics/archive/2010/08/03/1791626.html
下面为代码:
//本人是淳朴的C党
//x,y,z为光线向量的起点
//f为光线向量的方向
#include <stdio.h>
#include <math.h>
int n;
struct qiu
{
double xi,yi,zi,r;
}a[55]={0};
double x,y,z,fx,fy,fz;
double fx_,fy_,fz_;
int main()
{
int i,j;
double kx,ky,kz;
double ai,bi,ci;
double t,tt,ffx,ffy,ffz;
int pt,qt;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%lf%lf%lf%lf",&a[i].xi,&a[i].yi,&a[i].zi,&a[i].r);
scanf("%lf%lf%lf%lf%lf%lf",&x,&y,&z,&fx,&fy,&fz);
fx-=x;
fy-=y;
fz-=z;
qt=0;
for(i=1;i<=11;i++)
{
t=2e10;
pt=0;
for(j=1;j<=n;j++)
{
if(j==qt)
continue;
kx=x-a[j].xi;
ky=y-a[j].yi;
kz=z-a[j].zi;
ai=fx*fx+fy*fy+fz*fz;
bi=2*(kx*fx+ky*fy+kz*fz);
ci=kx*kx+ky*ky+kz*kz-a[j].r*a[j].r;
if(bi*bi<4*ai*ci)
continue;
tt=(-bi-sqrt(fabs(bi*bi-4*ai*ci)))/ai*0.5;
if(tt>0 && j!=qt)
{
if(tt<t)
{
t=tt;
pt=j;
}
}
else
{
tt=(-bi+sqrt(fabs(bi*bi-4*ai*ci)))/ai*0.5;
if(tt>0 && j!=qt && tt<t)
{
t=tt;
pt=j;
}
}
}
if(pt==0)
break;
else
{
if(i<=10)
printf("%d ",pt);
else
{
printf("etc.");
break;
}
qt=pt;
if(a[qt].r==0)
continue;
x+=t*fx;
y+=t*fy;
z+=t*fz;
fx*=-1;
fy*=-1;
fz*=-1;
ffx=x-a[qt].xi;//ff表示法线向量的方向
ffy=y-a[qt].yi;
ffz=z-a[qt].zi;
tt=(fx*ffx+fy*ffy+fz*ffz)/(ffx*ffx+ffy*ffy+ffz*ffz);
fx_=ffx*tt;//f_为反向后的入射向量在法线上的投影
fy_=ffy*tt;
fz_=ffz*tt;
fx=fx_+(fx_-fx);
fy=fy_+(fy_-fy);
fz=fz_+(fz_-fz);
}
}
printf("\n");
return 0;
}