题目大意:在一个三维空间中,给定一个球,给定两个点,询问这两个点的最短距离
解体思路:首先我们可以思考这样一种情况,两个点的连线没有经过球,显而易见我们可以想到球心到直线的距离大于半径;但是如果两点都在球的外面,但是球心到直线的距离小于R的时候需要特判,剩下的经过圆的只需要考虑到二维就行了
(因为球的任何一个面都是⚪)
#include <bits/stdc++.h>
using namespace std;
typedef long double ld;
const ld eps = 1e-8;
const ld pi = acos(-1);
int T;
ld ox,oy,oz,r;
ld sx,sy,sz,tx,ty,tz;
ld m,n,p;
ld t;
ld cal(ld xx,ld yy,ld zz,ld xxx,ld yyy,ld zzz)
{
ld anss = sqrtl((xx - xxx) * (xx - xxx) + (yy - yyy) * (yy - yyy) + (zz - zzz) * (zz - zzz));
return anss;
}
ld S(ld a,ld b,ld c)
{
ld p = (a + b + c) / 2;
return sqrt(p*(p-a)*(p-b)*(p-c));
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%Lf %Lf %Lf %Lf",&ox,&oy,&oz,&r);
scanf("%Lf %Lf %Lf %Lf %Lf %Lf",&sx,&sy,&sz,&tx,&ty,&tz);
if(sx == tx && sy == ty && sz == tz)
{
printf("0\n");
continue;
}
m = tx - sx;
n = ty - sy;
p = tz - sz;
t = (m * (ox - sx) + n * (oy - sy) + p * (oz - sz)) / (m * m + n * n + p * p);
//cout << t <<"\n";
ld dis1 = cal(ox,oy,oz,tx,ty,tz);
ld dis2 = cal(ox,oy,oz,sx,sy,sz);
ld crox,croy,croz;
crox = m * t + sx;
croy = n * t + sy;
croz = n * t + sz;
ld point_dis = cal(sx,sy,sz,tx,ty,tz);
ld s = S(dis1,dis2,point_dis);
ld line_dis = 2 * s / point_dis;
if(line_dis - r >= eps)
{
//cout <<"???"<<"\n";
printf("%.12Lf\n",point_dis);
}
else
{
if(dis1 > dis2) swap(dis1,dis2);
ld cosst;
cosst = (dis1 * dis1 + point_dis * point_dis - dis2 * dis2) / (2 * dis1 * point_dis);
if(cosst < 0)
{
printf("%.12Lf\n",point_dis);
}
else
{
ld ans1 = sqrtl(dis1 * dis1 - r * r);
ld ans2 = sqrtl(dis2 * dis2 - r * r);
//cout <<"????"<< ans1<<" "<<ans1<<" "<<"\n";
ld cosst1 = r / dis1;
ld cosst2 = r / dis2;
ld cosst3 = (dis1 * dis1 + dis2 * dis2 - point_dis * point_dis) / (2 * dis1 * dis2);
ld st1 = acos(cosst1);
ld st2 = acos(cosst2);
ld st3 = acos(cosst3);
ld st = st3 - st2 - st1;
//cout << "st" <<st1<<" "<<st2<<" "<<st3<<"\n";
ld ans3 = st * r;
ld ans = ans1 + ans2 + ans3;
printf("%.12Lf\n",ans);
}
}
}
return 0;
}