sgu-110 Dungeon(计算几何)

(写得丑不要吐槽,毕竟我是蒟蒻)

题目大意:

    给出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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值