sgu110-计算几何

110. Dungeon

time limit per test: 0.25 sec. 
memory limit per test: 4096 KB

The mission of space explorers found on planet M the vast dungeon. One of the dungeon halls is fill with the bright spheres. The explorers find out that the light rays reflect from the surface of the spheres according the ordinary law (the incidence angle is equal to the reflectance angle, the incidence ray, the reflected ray and the perpendicular to the sphere surface lay in the one plane). The ancient legend says that if the light ray will reflect from the spheres in the proper order, than the door to the room with very precious ancient knowledge will open. You are not to guess the right sequence; your task is much simpler. You are given the positions and the radii of the spheres, the place where the laser shot was made and the direction of light propagation. And you must find out the sequence in which the light will be reflected from the spheres.

Input

The first line of input contains the single integer n (1≤n≤50) - the amount of the spheres. The next n lines contain the coordinates and the radii of the spheres xi, yi, zi, ri (the integer numbers less or equal to 10000 by absolute value). The last line contains 6 real numbers - the coordinates of two points. The first one gives the coordinates of the place of laser shot, and the second gives the direction in which it was made (the second point is the point on the ray). The starting point of the ray lies strictly outside of any sphere.

Output

Your program must output the sequence of sphere numbers (spheres are numbers from 1 as they was given in input), from which the light ray was reflected. If the ray will reflect more the 10 times, than you must output first 10, then a space and the word 'etc.' (without quotes). Notice: if the light ray goes at a tangent to the sphere you must assume that the ray was reflected by the sphere.

Sample Input 1

1 
0 0 2 1 
0 0 0 0 0 1

Sample Output 1

1

Sample Input 2

2 
0 0 2 1 
0 0 -2 1 
0 0 0 0 0 100

Sample Output 2

1 2 1 2 1 2 1 2 1 2 etc.
 
题意:

宇宙探险家们在一次任务中发现了M行星上的一个巨大的地牢,地牢的一个大厅中充满了闪闪发光的小球。探险家们发现光线在小球表面反射时遵循光的镜面反射规律(入射角=反射角)。根据古老的传说,如果将光线以恰当的顺序在小球之间反射,那么通道一间隐藏着古老的珍贵知识的房间的门就会开启。放心,你的任务不是去猜这个恰当的顺序是什么。你的任务简单得多,你被告知小球的位置和半径,以及入射光线的光源位置和方向,然后你要找出光线在小球上反射的序列。

输入

第一行包含一个整数 n (1≤n≤50) 代表小球总数。 接下来 n 行包括小球的坐标和半径xi, yi, zi, ri (这些整数的绝对值不超过10000)。最后一行有6个实数 - 两个点的坐标。第一个点是入射点。第二个点表示光线的方向 (光线从第一个点直接指向第二个点)。光源不会在任何小球内部。

输出

你的程序必须输出小球编号组成的序列 (小球从1起按照输入的顺序编号),也就是光反射的顺序。如果光反射了超过10次,你只需要输出前10个再加上 'etc.' (引号不算)。 注意:如果光以切线方向射向小球,你也要认为他在球上反射了。

这里要注意的是最后一行输出,我以前第一个点是start point,第二个点是方向,没想到第二个点也是线上的,方向是point2-point1,我在这里debug2,3个小时......坑爹啊

我就说几个注意点吧

1.根据直线的参数方程,我们可以很容易把交点算出来

2.要注意精度问题,相切的时候也算反射,这时候b^2-4ac = 0在double的时候是不成立的,你可以设置一个1e-100

3.如何计算反射向量呢?(T_T 居然不给上传图片...)

我喜欢用在法线上取一点M,使得SM(start point)垂直法线

然后start point 关于M的对称点就是另一个点

4.如果光反射了超过10次,你只需要输出前10个再加上 'etc.' 如果恰好10次则不需要.

5.注意如果我上一次是1,那么这次计算的时候要排除1

TEST
3
0 0 2 1
0 0 5 1
0 0 8 1
0 1 0 0 1 1
ANSWER
1 2 3

TEST
3
1 0 2 1
1 0 3 1
1 0 4 1
0 0 0 0 0 1
ANSWER
1 2 3

my accept code

#include <iostream>
#include <cmath>
using namespace std;

#define ZERO 1e-10

inline double sqr(double x){return x*x;}
inline double min(double x1, double x2){return x1>x2 ? x2:x1;}

typedef struct _vector3f
{
	double x;
	double y;
	double z;
	_vector3f(){};
	_vector3f(double _x, double _y, double _z){x = _x;y = _y;z = _z;}
} vector3f;

typedef struct _sphere
{
	double x;
	double y;
	double z;
	double r;
	_sphere(){};
	_sphere(double _x, double _y, double _z, double _r){x = _x;y = _y;z = _z;r = _r;}
} Sphere;

/*
 * -1 means no root
 *  1 means we have root
 */
int calIntersaction(vector3f start, vector3f direction, Sphere sphere, double &t)
{
	double a = sqr(direction.x) + sqr(direction.y) + sqr(direction.z);

	double t1 = start.x - sphere.x;
	double t2 = start.y - sphere.y;
	double t3 = start.z - sphere.z;

	double b = 2.0 * (t1*direction.x + t2*direction.y + t3*direction.z);
	double c = sqr(t1) + sqr(t2) + sqr(t3) - sqr(sphere.r);

	double delta = sqr(b) - 4.0*a*c;
	//cout << "delta = " << delta << endl;
	if(delta < -ZERO)
		return -1;
	double root1 = ((-1.0 * b) + sqrt(delta)) / (2.0 * a);
    double root2 = ((-1.0 * b) - sqrt(delta)) / (2.0 * a);
    if(root1 < -ZERO)
    	return -1;
    
    t = min(root1, root2);
    return 1;
}
	
/*
 * center is the center of sphere
 * p is the intersection
 * incident is the incident ray
 */
vector3f calRefVector(Sphere center, vector3f p, vector3f start)
{
	vector3f normal = vector3f(p.x-center.x, p.y-center.y, p.z-center.z);
	
	//vector n1 = (p.x+t*n.x-p1.x, p.y+t*n.y-p1.y, p.z+t*n.z-p1.z) 
	//vector n2 = normal
	// n1*n2 = 0	
	double dx = p.x-start.x;
	double dy = p.y-start.y;
	double dz = p.z-start.z;
	
	double right = dx*normal.x + dy*normal.y + dz*normal.z;
	double left = sqr(normal.x) + sqr(normal.y) + sqr(normal.z);
	double t = -1.0*right/left;
	
	vector3f middle = vector3f(p.x+t*normal.x, p.y+t*normal.y, p.z+t*normal.z);
	vector3f anotherPoint = vector3f(2.0*middle.x-start.x, 2.0*middle.y-start.y, 2.0*middle.z-start.z);
	return vector3f(anotherPoint.x-p.x, anotherPoint.y-p.y, anotherPoint.z-p.z);
}

int main()
{
	int N;
	double x,y,z,r;
	Sphere myspheres[50];
	vector3f start,direction;
	cin >> N;
	for(int i = 0;i < N;i++){
		cin >> x >> y >> z >> r;
		myspheres[i] = Sphere(x,y,z,r);
	}
	cin >> x >> y >> z;
	start = vector3f(x,y,z);
	cin >> x >> y >> z;
	direction = vector3f(x - start.x, y-start.y, z-start.z);
	
	int lastNumber = -1;
	int i;
	for(i = 0;i < 11;i++){
		//find the nearest
		int reflectNumber = -1;
		double minium = 1.79769313486231570E+308;
		for(int k = 0;k < N;k++){
			double t;
			int result = calIntersaction(start, direction, myspheres[k], t);
			if((result != -1) && (lastNumber != k)){
				if(minium > t){
					minium = t;
					reflectNumber = k;
				}
			}
		}
		lastNumber = reflectNumber;
		
		if(reflectNumber != -1){
			if(i == 10){
				cout << "etc.";
				break;
			}
			cout << reflectNumber+1 << " ";
			vector3f intersection = vector3f(start.x+minium*direction.x, start.y+minium*direction.y, start.z+minium*direction.z);
			//update start , direction
			direction = calRefVector(myspheres[reflectNumber], intersection, start);
			start.x = intersection.x;
			start.y = intersection.y;
			start.z = intersection.z;
		}else{
			break;
		}
		
	}
	cout << endl;
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值