poj 1375

15 篇文章 0 订阅
4 篇文章 0 订阅

题目: Click here


题意:给出一个光源,给出一些圆,求投影区间。 就是求切线。


0 0 主要是最近在做几何的模版。所以贴一下。。


#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
const double eps = 1e-8;
const double inf = 1e12;
const int N = 11111;
struct point{
	double x,y;
	point(){x=0,y=0;}
	point(double _x,double _y){x=_x; y=_y;}
}light;
struct circle{
	point o;
	double r;
}Cir[N];
struct line{
	point p1,p2;
	line(){};
	line(point _p1,point _p2){
		p1 = _p1,p2 =_p2;
	}
};
bool equal(double x,double y){
	if(  y-x < eps && x - y < eps)
		return true;
	else return false;
}
struct node{
	double l, r;
	bool operator < (const node &tmp) const{
		if( (l<tmp.l) || (equal(l,tmp.l) && r<tmp.r)) return true;
		return false;
	};
}tp[N];
int n;
//通过圆外一点,求出两个切点pOn1,pOn2
void CalcQieDian(point pCenter, double r, point pOut,point &pOn1,point &pOn2) 
{ 
	point E,F,G;
 //1. 坐标平移到圆心ptCenter处,求园外点的新坐标E
	E.x= pOut.x-pCenter.x;
	E.y= pOut.y-pCenter.y; //平移变换到E
 
 //2. 求园与OE的交点坐标F, 相当于E的缩放变换
	double t= r / sqrt (E.x * E.x + E.y * E.y);  //得到缩放比例
 	F.x= E.x * t;   F.y= E.y * t;   //缩放变换到F

 //3. 将E旋转变换角度a到切点G,其中cos(a)=r/OF=t, 所以a=arccos(t);
 	double a1=acos(t),a2=-acos(t);   //得到旋转角度  
//double a=-acos(t); 得到另一个点
	G.x=F.x*cos(a1) -F.y*sin(a1);
	G.y=F.x*sin(a1) +F.y*cos(a1);    //旋转变换到G
 //5. 返回H
 	pOn1 = point(G.x+pCenter.x,G.y+pCenter.y);
//在计算一次
	G.x=F.x*cos(a2) -F.y*sin(a2);
	G.y=F.x*sin(a2) +F.y*cos(a2);    //旋转变换到G
 	pOn2 = point(G.x+pCenter.x,G.y+pCenter.y);
 //6. 实际应用过程中,只要一个中间变量E,其他F,G,H可以不用。
}
//通过圆外一点,求两条切线
void CalcQieLine(circle &o,point &out,line &l1,line &l2){
	point pOn1,pOn2;
	CalcQieDian(o.o,o.r,out,pOn1,pOn2);
	if(pOn2.x < pOn1.x){
		swap(pOn2,pOn1);
	}
	l1=line(out,pOn1);
	l2=line(out,pOn2);
}
//当y已经知道的时候,通过直线的y推导出x
double CalcPointUseLine(line &l,double y){
	return l.p2.x - (l.p2.y-y)*(l.p2.x-l.p1.x)/(l.p2.y-l.p1.y);
}
//返回投影的两个点
void CalcTouYing(circle &o,point &out,node &ans){
	line l1,l2;
	CalcQieLine(o,out,l1,l2);
	ans.l = CalcPointUseLine(l1,0.0);
	ans.r = CalcPointUseLine(l2,0.0);
}
int main(){
	while(scanf("%d",&n) && n){
		cin >> light.x >> light.y;
		for(int i = 0;i < n;i++){
			cin >> Cir[i].o.x >> Cir[i].o.y >> Cir[i].r;
		}
		for(int i = 0;i < n;i++){
			CalcTouYing(Cir[i],light,tp[i]);
		}
		sort(tp,tp+n);
		double L=tp[0].l,R=tp[0].r;
		for(int i = 1;i < n;i++){
			if(R < tp[i].l){
				printf("%.2lf %.2lf\n",L,R);
				L = tp[i].l, R = tp[i].r;
			}else{
				R = max(R,tp[i].r);
			}
		}printf("%.2lf %.2lf\n\n",L,R);
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值