题意:http://hi.baidu.com/hpfdf/item/4bc71df5cc9f3dc4a835a2a9
纯粹的数学题。初始一看非常麻烦,但是仔细思考后,其实并不复杂。需要注意一些细节。
1、用对称式的空间直线方程(一个点,一个空间向量)来表示入射光线,然后与球的方程联立方程组,求出比例系数t1,t2,再由系数求出直线与球的交点。需要注意的是:如果表示入射光线的点为起点的话,这里的系数应当是正数。另外如果系数是0的话,那么求出交点就是起点(或者说是上一个交点),直接用<=0判断的话会有误差(WA在第4组数据了),于是需要用一个变量pre来记录之前的交点。
2、由于入射直线可能与多个球体有交点,这里需要求出所有交点中,距离起点最近的那个。
3、求出交点后,便可由球心与交点求出法线向量,然后由入射向量和法线向量求出反射向量。
对于求反射向量,可参考:http://www.cnblogs.com/graphics/archive/2013/02/21/2920627.html
#include<cstdio>
#include<cmath>
#define eps 1e-10
using namespace std;
#define maxn 55
struct Point
{
double x,y,z;
Point (double xx,double yy,double zz) {x=xx;y=yy;z=zz;}
Point (){};
void read() {scanf("%lf%lf%lf",&x,&y,&z);}
};
struct Ball
{
Point C;
double r;
void read()
{
C.read();
scanf("%lf",&r);
}
}ball[maxn];
int n,pre;
int cmp(double a)
{
if(fabs(a)<eps) return 0;
return a>0?1:-1;
}
Point add(Point a,Point b) {return Point(a.x+b.x,a.y+b.y,a.z+b.z);}
Point dec(Point a,Point b) {return Point(a.x-b.x,a.y-b.y,a.z-b.z);}
double mul(Point a,Point b) {return a.x*b.x+a.y*b.y+a.z*b.z;}
double dis(Point a,Point b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y)+(a.z-b.z)*(a.z-b.z));}
double dis(Point a){return sqrt(a.x*a.x+a.y*a.y+a.z*a.z);}
void get(double &a,double &b,double &c,Ball B,Point L,Point S) //联立球与直线方程,得到一元二次方程系数
{
a=L.x*L.x+L.y*L.y+L.z*L.z;
b=2*((S.x-B.C.x)*L.x+(S.y-B.C.y)*L.y+(S.z-B.C.z)*L.z);
c=((S.x-B.C.x)*(S.x-B.C.x)+(S.y-B.C.y)*(S.y-B.C.y)+(S.z-B.C.z)*(S.z-B.C.z)-B.r*B.r);
}
bool solve(double a,double b,double c,double &x1,double &x2) //求解一元二次方程
{
if(cmp(b*b-4*a*c)<0) return 0;
x1=(-b+sqrt(b*b-4*a*c))/(a+a);
x2=(-b-sqrt(b*b-4*a*c))/(a+a);
if(x1<=0&&x2<=0) return 0; //由于这里以S为基准点,所求出的点应位于S->E方向,故求出的比例系数必须为正
return 1;
}
Point getline(Point a,Point b) {return dec(b,a);} //由两点得到直线向量
Point getrline(Point l,Point fl)//获取反射直线方程
{
double k=2*mul(l,fl)/(dis(fl)*dis(fl));
return dec(l,Point(fl.x*k,fl.y*k,fl.z*k));//反射向量,根据公式求出
}
int GetID(Point &l,Point &S) //求与入射直线相交的最近的球,参数为入射直线向量与入射直线上的一个点
{
Point p,p1,p2;
double a,b,c,t1,t2,d1,d2,Min=1e5;
int id=0;
for(int i=1;i<=n;++i) //求距离S最近的那个交点
{
if(i==pre) continue;
get(a,b,c,ball[i],l,S); //联立
if(!solve(a,b,c,t1,t2)) continue; //求解
p1=Point(S.x+t1*l.x,S.y+t1*l.y,S.z+t1*l.z);
p2=Point(S.x+t2*l.x,S.y+t2*l.y,S.z+t2*l.z);
d1=dis(S,p1);
d2=dis(S,p2);
if(t2>0&&cmp(d1-d2)>=0)
{
d1=d2;
p1=p2;
}
if(cmp(Min-d1)>0)
{
id=i;
Min=d1;
p=p1;
}
}
if(!id) return 0;
pre=id;
Point fl=getline(ball[id].C,p);//法线向量
Point rl=getrline(l,fl);
l=rl;
S=p;
return id;
}
int main()
{
Point S,E,l;
int i,id;
scanf("%d",&n);
for(i=1;i<=n;++i) ball[i].read();
S.read(),E.read();
l=getline(S,E);
pre=-1;
for(i=1;;++i)
{
id=GetID(l,S);
if(!id) break;
if(i==11) {printf("etc.");break;}
printf("%d ",id);
}
puts("");
return 0;
}