POJ 3944 - Spherical Mirrors
http://poj.org/problem?id=3944
概要
三次元空間に N 個の球がある.この球は図のようにレーザーを反射する.
(0,0,0) から (u,v,w) の方向にレーザーを発射したとき,最後に反射する球面上の点を答える.
制約
- 1 <= N <= 100
- -100 <= 座標 <= 100
- 5 <= 半径 <= 30
- u^2 + v^2 + w^2 > 0
- どの2つの球の球面も 0.1 以上離れている
- (0,0,0) はどの球にも含まれておらず,どの球面からも 0.1 以上離れている
- 光線は少なくとも1回反射し,多くとも5回しか反射しない
- 反射するときの反射角 θ は85度未満
解法
三次元幾何をがんばる. ライブラリを用意できていれば特に難しい点は無い.
半直線と球の交差判定は,半直線と円の交差判定と同様に,向きを確認してから点と直線の距離が r 未満かどうかを見ればいい.
半直線と球の交点は二分探索で求めた.
反射させるときは,2回の外積で QN に関して QI とは逆方向の法線を出して,QI と QN の内積で得られた cosθ を使って QR を作った. ただし,θ = 0 のときは QI と QN の外積が零ベクトルになってしまうので,単に入射光のベクトルを反転させたものを返すようにした.
poj/3944.cc1 #include <cstdio> 2 #include <vector> 3 #include <map> 4 #include <cmath> 5 using namespace std; 6 static const double EPS = 1e-6; 7 8 struct P 9 { 10 double x, y, z; 11 P() {} 12 P(double a, double b, double c) : x(a), y(b), z(c) {} 13 P operator+(const P& that) const { return P(x + that.x, y + that.y, z + that.z); } 14 P operator-(const P& that) const { return P(x - that.x, y - that.y, z - that.z); } 15 P& operator*=(double a) { x *= a; y *= a; z *= a; return *this; } 16 P operator*(double a) const { P p(*this); p *= a; return p; } 17 P& operator/=(double a) { x /= a; y /= a; z /= a; return *this; } 18 P operator/(double a) const { P p(*this); p /= a; return p; } 19 20 double abs() const { return sqrt(x*x + y*y + z*z); } 21 double dot(const P& that) const { return x*that.x + y*that.y + z*that.z; } 22 P cross(const P& that) const 23 { 24 return P( 25 y*that.z - that.y*z, 26 z*that.x - that.z*x, 27 x*that.y - that.x*y); 28 } 29 }; 30 31 struct line 32 { 33 P a, b; 34 line(const P& x, const P& y) : a(x), b(y) {} 35 }; 36 37 struct sphere 38 { 39 P o; 40 double r; 41 sphere(const P& p, double x) : o(p), r(x) {} 42 43 bool intersects(const line& ln) const 44 { 45 const P ab = ln.b - ln.a; 46 const P ao = o - ln.a; 47 if (ab.dot(ao) < 0) { 48 return false; 49 } 50 const double h = ab.cross(ao).abs() / ab.abs(); 51 return h < r; 52 } 53 54 P intersection(const line& ln) const 55 { 56 const P ab = ln.b - ln.a; 57 const P ao = o - ln.a; 58 const double h = ab.cross(ao).abs() / ab.abs(); 59 const double d = ao.abs(); 60 const double l = sqrt(d*d - h*h); 61 double left = 0.0, right = l/ab.abs(); 62 for (int i = 0; i < 100; i++) { 63 const double mid = (left + right)/2.0; 64 const P ap = ab*mid; 65 const double t = (ap - ao).abs(); 66 if (t < r) { 67 right = mid; 68 } else { 69 left = mid; 70 } 71 } 72 return ln.a + ab*((left + right)/2.0); 73 } 74 75 line reflect(const line& ln) const 76 { 77 const P q = intersection(ln); 78 const P qa = ln.a - q; 79 P qn = q - o; 80 qn /= qn.abs(); 81 const P x = qn.cross(qa); 82 if (x.abs() < EPS) { 83 return line(q, ln.a); 84 } 85 P qm = qn.cross(x); 86 qm /= qm.abs(); 87 const double t = qn.dot(qa)/(qn.abs()*qa.abs()); 88 const P qr = qm*sqrt(1-t*t) + qn*t; 89 return line(q, q + qr); 90 } 91 }; 92 93 int main() 94 { 95 int N; 96 while (scanf("%d", &N) != EOF && N != 0) { 97 int u, v, w; 98 scanf("%d %d %d", &u, &v, &w); 99 line ln(P(0, 0, 0), P(u, v, w)); 100 vector<sphere> ss; 101 for (int i = 0; i < N; i++) { 102 int x, y, z, r; 103 scanf("%d %d %d %d", &x, &y, &z, &r); 104 ss.push_back(sphere(P(x, y, z), r)); 105 } 106 107 P ans; 108 for (;;) { 109 map<double,int> m; 110 for (int i = 0; i < N; i++) { 111 if (ss[i].intersects(ln)) { 112 const P p = ss[i].intersection(ln); 113 m.insert(make_pair((p - ln.a).abs(), i)); 114 } 115 } 116 if (m.empty()) { 117 break; 118 } 119 const sphere& s = ss[m.begin()->second]; 120 ln = s.reflect(ln); 121 ans = ln.a; 122 } 123 printf("%.4f %.4f %.4f\n", ans.x, ans.y, ans.z); 124 } 125 return 0; 126 }