已知行星球心,恒星球心,摄像机这3个点的坐标,和行星,恒星的半径。问摄像机能够拍到的行星的范围内有没有一个面积为正的区域被恒星照亮。
首先把这三者做一下坐标转换。以行星球心为原点,行星球心向恒星球心为x轴正方向,摄像机所在位置为y轴正方向所在区域。把这个图转化到一个二维平面中。
记行星半径为r,恒星半径为R,行星和恒星的球心距为a,恒星与摄像机距离为b,行星与摄像机距离为c。行星坐标为原点(0,0),恒星坐标为(a,0),可以求出摄像机坐标为(x,y)。
可以求出恒星能照亮的角度范围为[-alpha,+alpha],以及摄像机能够看到的范围[atan2-acos(r/c),atan2+acos(r/c)]。
所以可以得出,如果belta=atan2-acos(r/c)<alpha,则有面积为正的区域。如果belta==alpha,则有面积为0的区域(一个交点...)。如果belta>alpha,则没有交叉区域。
#include <cstdio>
#include <cmath>
#include <iostream>
using std::cin;
using std::cout;
using std::endl;
const long double PI2=asin((long double)1);
const long double eps=1e-11;
long double xx1,yy1,zz1,r;
long double xx2,yy2,zz2,R;
long double xx3,yy3,zz3;
long double a,b,c;
long double xx,yy,alpha,beta;
long double len(long double xx,long double yy,long double zz=0) {
return sqrt(xx*xx+yy*yy+zz*zz);
}
long double calAlpha() {
if (R>r) return PI2+asin((R-r)/a);
else acos((r-R)/a);
}
void calPos() {
long double p=(a+b+c)/2;
long double s=sqrt(p*(p-a)*(p-b)*(p-c));
long double h=s*2/a;
long double l=sqrt(c*c-h*h);
yy=h;
//printf("a=%Lf b=%Lf c=%Lf\n",a/1e7,b/1e7,c/1e7);
//printf("p=%Lf s=%Lf\n",p/1e7,s/1e7);
//printf("h=%Lf l=%Lf cos=%Lf\n",h/1e7,l/1e7,(a*a+c*c-b*b)/1e7);
if (a*a+c*c-b*b>0) {
xx=l;
} else {
xx=-l;
}
}
long double calBeta() {
return atan2(yy,xx)-acos(r/c);
}
int main() {
cin >> xx1 >> yy1 >> zz1 >> r;
cin >> xx2 >> yy2 >> zz2 >> R;
cin >> xx3 >> yy3 >> zz3;
a=len(xx1-xx2,yy1-yy2,zz1-zz2);
b=len(xx3-xx2,yy3-yy2,zz3-zz2);
c=len(xx1-xx3,yy1-yy3,zz1-zz3);
alpha=calAlpha();
calPos();
beta=calBeta();
//printf("a=%Lf b=%Lf c=%Lf\n",a/1e7,b/1e7,c/1e7);
//printf("r=%Lf R=%Lf\n",r/1e7,R/1e7);
//printf("x=%Lf y=%Lf\n",xx/1e7,yy/1e7);
//printf("alpha=%Lf beta=%Lf\n",alpha,beta);
//printf("PI/2=%Lf PI=%Lf\n",PI2,PI2*2);
if (c<=r+eps) cout << "No" << endl;
else if (alpha>beta+eps) cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}