AOJ 1151 Twirl Around(くるくる)

問題

日本語なので本文参照(http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=1151&lang=jp


多角形の内側にそって線分を回転させて、2*PI*Rラジアンまわした後の座標を答える。
棒がつっかかったらそこで終了。

方針

シミュレートする。
線分が次にぶつかる場所を求め、回転してそこまでぶつけて、
そこを中心にしてまた次にぶつかる場所を……と次々にやっていけばよい。


0距離でぶつかるようになってしまったら、つっかえているということなので終了。
ただし、離れる向きで最初から接触している分には問題なくて、
めり込む向きに最初から接しているのだけがダメ。


次にぶつかる場所はどうやって求めるかというと、

  • 回している線分の端点が多角形の辺にぶつかる場合
  • 回している線分の途中が多角形の頂点にぶつかる場合

の両方の場合が考えられて、前者は円と線分の交点で求まり、後者は点と直線の距離を使えばよい。


交点がもとまったら、ぶつかる角度の順にソートして、
一番小さいものが次にぶつかる点。同じ角度が複数あったら一番遠いところ。
で、最初からくっついている場合があるが、離れる向きのものはセーフ(離れるだけなので無視してよい)で、めりこむ向きのものがあったら、つっかかるのでそこでループ打ち切り。


離れる向きかめり込む向きかは、上の前者の場合と後者の場合で判定方法が違って、

  • 前者なら、(回転中心)-(接点)-(多角形の接点の次の頂点)が時計回りならめりこむ
  • 後者なら、微小な角度だけ回転させて、内部から出てしまったらアウト。


バグったら頑張って半自動で図示するビジュアライザー作って確認する(した)。

ソースコード

long double len, R;
int n;
 
inline double arg_(const P &p){
	double a = arg(conj(p));
	while(a < -EPS) a += 2 * PI;
	return a;
}
inline void rot(P &p, const P &c, double a){
	p = P(cos(a), sin(a)) * (p - c) + c;
}
 
int main(){
	while(cin >> len >> R >> n, n){
		R *= 2 * PI;
		 
		G g;
		rep(i, n){
			double x, y; cin >> x >> y;
			g.pb(P(x, y));
		}
		L bar(P(0, 0), P(0, len));
		P c(0, 0);
		 
		 
		int loop = 0;
		 
		while(R > EPS){
			//dbg2(bar[0], bar[1]);
			 
			long double r0 = abs(bar[0] - c);
			long double r1 = abs(bar[1] - c);
			 
			vector<pair<long double, P> > p0;
			vector<pair<long double, P> > p1;
			P next(c);
			long double nd = 0;
			 
			//頂点との交わり
			rep(i, n){
				long double d = abs(g[i] - c);
				if(d < EPS) continue;
				if(d - EPS < r0){
					 
					long double a = arg_((g[i] - c) / (bar[0] - c));
					if(a > EPS) p0.pb(mp(a, g[i]));
					else if(ccw(c, g[i], g[(i + 1) % n]) == -1){
						R = -1; break;
					}
				}
				if(d - EPS < r1){
					 
					long double a = arg_((g[i] - c) / (bar[1] - c));
					if(a > EPS) p1.pb(mp(a, g[i]));
					else if(ccw(c, g[i], g[(i + 1) % n]) == -1){
						R = -1; break;
					}
				}
			}
			
			//辺との交わり
			rep(i, n){
				const P &p = g[i];
				const P &q = g[(i + 1) % n];
				G g0 = isCL(c, r0, p, q);
				G g1 = isCL(c, r1, p, q);
				 
				if(r0 > EPS) rep(j, g0.size()){
					if(ccw(g0[j], p, q) != 2) continue;
					 
					long double a = arg_((g0[j] - c) / (bar[0] - c));
					if(a > EPS) p0.pb(mp(a, g0[j]));
					else{
						rot(g0[j], c, -.00005);
						if(contains(g, g0[j]) != IN){
							R = -1; break;
						}
					}
					 
				}
				if(r1 > EPS) rep(j, g1.size()){
					if(ccw(g1[j], p, q) != 2) continue;
					 
					long double a = arg_((g1[j] - c) / (bar[1] - c));
					if(a > EPS) p1.pb(mp(a, g1[j]));
					else{
						rot(g1[j], c, -.00005);
						if(contains(g, g1[j]) != IN){
							R = -1; break;
						}
					}
				}
			}
			if(R < EPS) break;
			if(!p0.size() && !p1.size()) break; //??????
			sort(all(p0)); sort(all(p1));
			 
			p0.insert(p0.end(), all(p1));
			sort(all(p0));
			 
			rep(i, p0.size()) if(abs(p0[i].first - p0[0].first) < EPS){
				long double d = abs(p0[i].second - c);
				if(d > nd + EPS){
					nd = d;
					next = p0[i].second;
				}
			}
			 
			long double ang = min(p0[0].first, R);
			R -= ang;
			rot(bar[0], c, -ang);
			rot(bar[1], c, -ang);
			c = next;
		}
		printf("%.9f %.9f\n", (double)bar[0].real(), (double)bar[0].imag());
	}
	 
	return 0;
}