struct Circle{
Point c;
double r;
Circle(Point cc,double rr):c(cc),r(rr){}
Point point(double a){//通过圆心角a求圆上坐标
return Point(c.x+cos(a)*r,c.y+sin(a)*r);
}
};
//直线和圆交点,解一元二次方程,注意sol没有清空
int getLineCircleIntersection(Point P,Vector v,Circle C,double& t1,double& t2,vector<Point>& sol){
double a=v.x,b=P.x-C.c.x,c=v.y,d=P.y-C.c.y;
double e=a*a+c*c,f=2*(a*b+c*d),g=b*b+d*d-C.r*C.r;
double delta=f*f-4*e*g;//判别式
if(dcmp(delta)<0) return 0;//相离
if(dcmp(delta==0)){//相切
t1=t2=-f/(2*e);
sol.push_back(P+v*t1);
return 1;
}
//相交
t1=(-f-sqrt(delta))/(2*e);sol.push_back(P+v*t1);
t2=(-f+sqrt(delta))/(2*e);sol.push_back(P+v*t2);
return 2;
}
//向量极角
double angle(Vector v){ return atan2(v.y,v.x); }
//两圆交点,注意sol没有清空
int getCircleCircleIntersection(Circle C1,Circle C2,vector<Point>& sol){
double d=Length(C1.c-C2.c);
if(dcmp(d)==0){
if(dcmp(C1.r-C2.r)==0) return -1;//两圆重合
return 0;//内含(同心圆)
}
if(dcmp(C1.r+C2.r-d)<0) return 0;//外离
if(dcmp(fabs(C1.r-C2.r)-d)>0) return 0;//内含
double a=angle(C2.c-C1.c);//C1C2极角
double da=acos((C1.r*C1.r+d*d-C2.r*C2.r)/(2*C1.r*d));
Point p1=C1.point(a-da),p2=C1.point(a+da);
sol.push_back(p1);
if(p1==p2) return 1;//相切(内切或外切)
sol.push_back(p2);
return 2;//相交
}
//过点p到圆C的切线,v[i]是第i条切线的向量,返回切线条数
int getTangents(Point p,Circle C,Vector* v){
Vector u=C.c-p;
double dist=Length(u);
if(dist<C.r) return 0;//圆内无切线
else if(dcmp(dist-C.r)==0){//p在圆上,只有一条切线
v[0]=Rotate(u,PI/2);
return 1;
}
else{
double ang=asin(C.r/dist);
v[0]=Rotate(u,-ang);
v[1]=Rotate(u,+ang);
return 2;
}
}
//两圆公切线,返回切线条数,-1表示无穷多条
//a[i],b[i]分别是第i条切线在圆A和圆B上的切点
int getTangents(Circle A,Circle B,Point* a,Point* b){
int cnt=0;
if(A.r<B.r){ swap(A,B);swap(a,b); }
double d=Length(B.c-A.c);
double rdiff=A.r-B.r;
double rsum=A.r+B.r;
if(dcmp(d-rdiff)==-1) return 0;//内含
double base=angle(B.c-A.c);
if(dcmp(d)==0 && dcmp(A.r-B.r)==0) return -1;//无数条公切线
if(dcmp(d-rdiff)==0){//内切
a[cnt]=A.point(base);
b[cnt]=B.point(base);
++cnt;
return 1;
}
//有外公切线
double ang=acos((A.r-B.r)/d);
a[cnt]=A.point(base+ang);b[cnt]=B.point(base+ang);++cnt;
a[cnt]=A.point(base-ang);b[cnt]=B.point(base-ang);++cnt;
if(dcmp(d-rsum)==0){//一条内公切线
a[cnt]=A.point(base);
b[cnt]=B.point(base+PI);
++cnt;
}
else if(dcmp(d-rsum)>0){//两条内公切线
ang=acos((A.r+B.r)/d);
a[cnt]=A.point(base+ang);b[cnt]=B.point(base+ang+PI);++cnt;
a[cnt]=A.point(base-ang);b[cnt]=B.point(base-ang+PI);++cnt;
}
return cnt;
}