圆的反演与求公切圆

什么是圆的反演

给定一个圆心为 C C C半径为 R R R的圆。对于在圆心 C C C同侧的两个点 A A A A ′ A' A,若 ∣ C A ∣ ∗ ∣ C A ′ ∣ = R 2 |CA|*|CA'|=R^2 CACA=R2,则说 A A A A ′ A' A互为关于圆 C C C的反演点。

在经过一次反演后,圆 C C C内的点都到了圆外,圆外的点都到了圆内。

C C C被称为反演中心 R R R被称为反演半径

圆的反演有什么用

圆的反演的性质:

过反演中心的圆,反形是不过反演中心的相交直线。不过反演中心的圆,反形还是圆。反演中心的不过反演中心的直线,反形是反演中心的圆。

我们都知道,圆的公切圆很不好求,但是公切线(相对)很好求。所以如果我想求两圆过定点的公切圆,只要在以定点为反演中心做反演后,求两圆的公切线,再把公切线反演回去就好了。

具体实现

反演一个圆

如图所示, C 1 C_1 C1是原图形, C 2 C_2 C2是反形, O O O是反演中心, R R R是反演半径, A 1 A_1 A1 A 2 A_2 A2互为反演点, B 1 B_1 B1 B 2 B_2 B2互为反演点。

灵魂画手litble
不难发现

∣ O A ∣ ∗ ∣ O A ′ ∣ = ( ∣ O C 1 ∣ − r 1 ) ( ∣ O C 2 ∣ + r 2 ) = R 2 |OA|*|OA'|=(|OC_1|-r_1)(|OC_2|+r_2)=R^2 OAOA=(OC1r1)(OC2+r2)=R2

∣ O B ∣ ∗ ∣ O B ′ ∣ = ( ∣ O C 1 ∣ + r 1 ) ( ∣ O C 2 ∣ − r 2 ) = R 2 |OB|*|OB'|=(|OC_1|+r_1)(|OC_2|-r_2)=R^2 OBOB=(OC1+r1)(OC2r2)=R2

所以

r 2 = R 2 2 ( 1 ∣ O C 1 ∣ − r 1 − 1 ∣ O C 1 ∣ + r 1 ) r_2=\frac{R^2}{2}(\frac{1}{|OC_1|-r_1}-\frac{1}{|OC_1|+r_1}) r2=2R2(OC1r11OC1+r11)

∣ O C 2 ∣ = R 2 2 ( 1 ∣ O C 1 ∣ − r 1 + 1 ∣ O C 1 ∣ + r 1 ) |OC_2|=\frac{R^2}{2}(\frac{1}{|OC_1|-r_1}+\frac{1}{|OC_1|+r_1}) OC2=2R2(OC1r11+OC1+r11)

然后就不难求出反形了。

反演一条直线

作反演中心到该直线的垂线,由于垂足是直线上离反演中心最近的点,所以在反演后,它也是反形(圆)上离反演中心最远的点。将垂足的反演点求出后,就不难求出反形的半径和圆心了。

其他

有的反形的公切线是不能转化为公切圆的,因为可能转化后会是半径为无穷大的公切圆,那么也就是反形的公切线若过反演中心,就不能转化为公切圆。

代码

#include <bits/stdc++.h>
using namespace std;
#define RI register int
typedef double db;
const db R=1,eps=1e-9;
int T,cnt;
struct point{db x,y;}P;
point operator + (point A,point B) {return (point){A.x+B.x,A.y+B.y};}
point operator - (point A,point B) {return (point){A.x-B.x,A.y-B.y};}
point operator / (point A,db B) {return (point){A.x/B,A.y/B};}
point operator * (point A,db B) {return (point){A.x*B,A.y*B};}
db operator & (point A,point B) {return A.x*B.x+A.y*B.y;}
db operator * (point A,point B) {return A.x*B.y-A.y*B.x;}
struct circle{point o;db r;}c1,c2,ans[6];

#define sqr(x) ((x)*(x))
db dist(point A,point B) {return sqrt(sqr(A.x-B.x)+sqr(A.y-B.y));}
db mo(point A) {return sqrt(sqr(A.x)+sqr(A.y));}
point rot(point A,db ang)
	{return (point){A.x*cos(ang)-A.y*sin(ang),A.x*sin(ang)+A.y*cos(ang)};}
circle inversion_circle(circle c) {//反演一个圆
	db d=dist(c.o,P),k1=1.0/(d-c.r),k2=1.0/(d+c.r);
	circle re;point v=(c.o-P)/d;
	re.r=R*R/2.0*(k1-k2);
	re.o=P+v*(R*R/2.0*(k1+k2));
	return re;
}
void inversion_line(point A,point B) {//反演一条直线
	point v=B-A,H=A+v*(((P-A)&v)/sqr(mo(v)));
	++cnt;point kv=(H-P)*sqr(R/dist(H,P));
	ans[cnt].o=P+kv*0.5,ans[cnt].r=mo(kv)/2.0;
}
void work() {
	c1=inversion_circle(c1),c2=inversion_circle(c2);
	if(c1.r<c2.r) swap(c1,c2);
	
	//求外公切线:小圆的圆心往大圆与切线垂直的半径做垂线
	point v=c2.o-c1.o;
	db d=dist(c1.o,c2.o),a1=atan2(v.y,v.x),a2=acos((c1.r-c2.r)/d);
	point k1=(point){c1.o.x+cos(a1+a2)*c1.r,c1.o.y+sin(a1+a2)*c1.r};
	point k2=(point){c2.o.x+cos(a1+a2)*c2.r,c2.o.y+sin(a1+a2)*c2.r};
	if(fabs((P-k1)*(k2-k1))>eps) inversion_line(k1,k2);
	point k3=(point){c1.o.x+cos(a1-a2)*c1.r,c1.o.y+sin(a1-a2)*c1.r};
	point k4=(point){c2.o.x+cos(a1-a2)*c2.r,c2.o.y+sin(a1-a2)*c2.r};
	if(fabs((P-k3)*(k4-k3))>eps) inversion_line(k3,k4);
	
	//求内公切线:先求出两圆内公切线交点,然后用求点到圆的切线的方法求
	db kd1=d*c1.r/(c1.r+c2.r),kd2=d*c2.r/(c1.r+c2.r);
	point tangent_intersection=c1.o+(c2.o-c1.o)*(kd1/d);
	db a3=asin(c1.r/dist(tangent_intersection,c1.o));
	point v0=c1.o-tangent_intersection;
	point v1=rot(v0,a3),v2=rot(v0,-a3);
	v1=v1/mo(v1),v2=v2/mo(v2);
	point k5=tangent_intersection+v1*kd1,k6=tangent_intersection-v1*kd2;
	if(fabs((P-k5)*(k6-k5))>eps) inversion_line(k5,k6);
	point k7=tangent_intersection+v2*kd1,k8=tangent_intersection-v2*kd2;
	if(fabs((P-k7)*(k8-k7))>eps) inversion_line(k7,k8);
}
int main()
{
	scanf("%d",&T);
	while(T--) {
		scanf("%lf%lf%lf",&c1.o.x,&c1.o.y,&c1.r);
		scanf("%lf%lf%lf",&c2.o.x,&c2.o.y,&c2.r);
		scanf("%lf%lf",&P.x,&P.y);
		cnt=0,work();
		printf("%d\n",cnt);
		for(RI i=1;i<=cnt;++i)
			printf("%.8f %.8f %.8f\n",ans[i].o.x,ans[i].o.y,ans[i].r);
	}
	return 0;
}```
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值