poj1375 圆的切线

给一个源点s,给一些圆,源点和s相切会形成阴影,求阴影并。


如果能求出所有的圆构成的阴影,sort扫一遍就好了。


怎么求,我们可以看成是s到圆的切线的直线和x正半轴求交点。

我们可以计算圆心到s的距离,也可以得到那条直线,利用arcsin(r/d)得到一个角度,把直线旋转这个角度就得到了s和圆的切线(顺指针逆时针各得到一条),一段阴影就求到了。


多段阴影拆成进边和出边,然后sort扫一遍(类似于扫描线)。


#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
const int maxn=505;
using namespace std;
struct point
{
	double x,y,r;
	int k;
	point(){}
	point (double _x,double _y)
	{
		x=_x;
		y=_y;
	}
	point operator +(const point &b)
	{
		return point(x+b.x,y+b.y);
	}
	point operator -(const point &b)
	{
		return point(x-b.x,y-b.y);
	}
	point operator *(const double &b)
	{
		return point(x*b,y*b);
	}
	bool operator <(const point &b)const
	{
		return x<b.x;
	}
};
struct line
{
	point a,b;
	line(){}
	line(point _a,point _b)
	{
		a=_a;
		b=_b;
	}
};
double dot(point a,point b)
{
	return a.x*b.x+a.y*b.y;
}
double cross(point a,point b)
{
	return a.x*b.y-a.y*b.x;
}
point rot(point a,double rad){return point(a.x*cos(rad)+a.y*sin(rad),a.y*cos(rad)-a.x*sin(rad));}
double lenth(point a){return sqrt(dot(a,a));}
int n;
point p[maxn];
point S;
line L[maxn];
vector<point>q;
point getsec(point p1,point v1,point p2,point v2)
{
	double x=cross(p2-p1,v2)/cross(v1,v2);
	return v1*x+p1;
}
void work(point a)
{
	point v=a-S;
	double rad=asin(a.r/lenth(v));
	point p1=getsec(point(0,0),point(1,0),S,rot(v,rad));
	point p2=getsec(point(0,0),point(1,0),S,rot(v,-rad));
	p1.k=1,p2.k=-1;
	q.push_back(p1);
	q.push_back(p2);
}
void solve()
{
	sort(q.begin(),q.end());
	int kk=0;
	int i=0;
	while(i<q.size())
	{
		kk+=q[i].k;
		if(kk)
		{
			double st=q[i].x;	
			i++;			
			while(i<q.size())
			{
				kk+=q[i].k;
				if(kk==0)break;
				i++;
			}
			double ft=q[i].x;
			printf("%.2f %.2f\n",st,ft);
		}
		i++;
	}
}
int main()
{
	while(scanf("%d",&n)!=EOF&&n)
	{
		scanf("%lf%lf",&S.x,&S.y);
		q.clear();
		for(int i=1;i<=n;i++)
		{
			scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].r);
			work(p[i]);
		}
		solve();
		printf("\n");
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值