基础计算几何模板+解释

这个部分模板是来自kuagnbin模板,我为了能够好理解加了些解释和说明

但是由于下面代码全部是我自己纯手抄的,所以只能保证编译通过,不能保证100%正确,如有错误希望大家指正,所以如果比赛使用模板仍然以kuangbin模板为准,我的这篇仅供理解

首先在开始前需要定义一些变量,和几个必要的方法

#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-8;
const double inf = 1e20;
const double pi = acos( 1.0);//圆周率
const int maxp = 1010;

//和0比较大小的函数
int sgn(double x){
    if(fabs(x) < eps)return 0;//等于0
    if(x < 0)return -1;//小于0
    else return 1; //大于0
}

//浮点数平方
inline double sqr(double x){return x*x;}

一、与点相关的公式

首先定义点的结构体,并对必要运算符进行重载得到点坐标的基本运算

struct Point{
    double x,y;
    Point(){}
    Point(double _x,double _y){
        x = _x;
        y = _y;
    }//构造函数
    void input(){//输入
        scanf("%lf%lf",&x,&y);
    }
    void output(){//输出
        printf("%.2f %.2f\n",x,y);//保留位数可自己控制
    }
    bool operator == (Point b)const{//横纵坐标均相等
        return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
    }
    bool operator < (Point b)const{//先比横坐标,相同在比较纵坐标
        return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
    }
    Point operator - (const Point &b)const{
        return Point(x - b.x, y - b.y);
    }
    Point operator + (const Point &b)const{
        return Point(x + b.x, y + b.y);
    }
    //叉积
    double operator ^ (const Point &b)const{
        return x * b.y - y * b.x;
    }
    //点积
    double operator * (const Point &b)const{
        return x * b.x + y * b.y;
    }
    //数乘
    Point operator * (const double &k)const{
        return Point(x * k, y * k);
    }
    Point operator / (const double &k)const{
        return Point(x / k, y / k);
    }
    //返回长度
    double len(){
        return hypot(x,y);//<math.h>中函数,给定直角三角形两直角边求出斜边长度
    }
    //返回长度平方
    double len2(){
        return x * x + y * y;
    }
    //返回两点距离
    double dis(Point p){
        return hypot(x - p.x, y - p.y);
    }
};

1.计算向量 p a p b 的夹角

这里写图片描述
我们知道在直角坐标系中,知道一个点坐标我们可以计算其角度如下图所示
这里写图片描述

t a n ( θ ) = y x

利用 a t a n ( ) 反正切函数可以求出角度
θ = a t a n ( y x )

但是我们使用一个更好的函数 a t a n 2 ( ) 这个函数同样是反正切函数,但是要比 a t a n ( ) 更稳定

所以

θ = a t a n 2 ( y , x )

对于一般情况只要求出一个投影和投影高度就可以利用函数求啦
这里写图片描述
我们可以这样求

| p a × p b | = | p a | | p b | s i n θ

| p a p b | = | p a | | p b | c o s θ

| p a | | p b | s i n θ | p a | | p b | c o s θ = t a n θ

所以函数中只需传入叉积和点积的绝对值即可
模板:

//计算pa和pb的夹角
    double rad(Point a,Point b){
        Point p = *this;
        return fabs(atan2(fabs((a - p) ^ (b - p)),(a - p) * (b - p)));
    }

2.转化成长度为r的向量

模板:

//化为长度为r的向量
    Point turn(double r){
        double l = len();
        if(!sgn(l)) return *this;//如果原来长度为0,直接返回这个点
        r /= l;
        return Point(x * r,y * r);
    }

假设原向量 ( x , y ) ,改变后为 ( x r l , y r l )

其长度为

x 2 r 2 + y 2 r 2 l 2 = ( x 2 + y 2 ) r 2 l 2 = l 2 r 2 l 2 = r

3.点的旋转

逆时针旋转90°和顺时针旋转90°

这里写图片描述
模板:

//逆时针旋转90
    Point rotleft(){
        return Point(-y,x);
    }
    //顺时针旋转90
    Point rotright(){
        return Point(y,-x);
    }

x y 绕点 ( r x 0 , r y 0 ) 逆时针旋转任意角度 α 有公式:

x 0 = r x 0 + ( x r x 0 ) c o s α ( y r y 0 ) s i n α

y 0 = r y 0 + ( x r x 0 ) s i n α + ( y r y 0 ) c o s α

证明先不证明了,不会。。。网上应该有看着很麻烦就不写了
模板

//绕p点逆时针旋转angle
    Point rot(Point p,double angle){
        Point v = (*this) - p;
        double c = cos(angle), s = sin(angle);
        return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
    }

以上就是关于点的一些操作的模板,下面是代码汇总(全都在一个结构体中)

struct Point{
    double x,y;
    Point(){}
    Point(double _x,double _y){
        x = _x;
        y = _y;
    }//构造函数
    void input(){//输入
        scanf("%lf%lf",&x,&y);
    }
    void output(){//输出
        printf("%.2f %.2f\n",x,y);//保留位数可自己控制
    }
    bool operator == (Point b)const{//横纵坐标均相等
        return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
    }
    bool operator < (Point b)const{//先比横坐标,相同在比较纵坐标
        return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
    }
    Point operator - (const Point &b)const{
        return Point(x - b.x, y - b.y);
    }
    Point operator + (const Point &b)const{
        return Point(x + b.x, y + b.y);
    }
    //叉积
    double operator ^ (const Point &b)const{
        return x * b.y - y * b.x;
    }
    //点积
    double operator * (const Point &b)const{
        return x * b.x + y * b.y;
    }
    //数乘
    Point operator * (const double &k)const{
        return Point(x * k, y * k);
    }
    Point operator / (const double &k)const{
        return Point(x / k, y / k);
    }
    //返回长度
    double len(){
        return hypot(x,y);//<math.h>中函数,给定直角三角形两直角边求出斜边长度
    }
    //返回长度平方
    double len2(){
        return x * x + y * y;
    }
    //返回两点距离
    double dis(Point p){
        return hypot(x - p.x, y - p.y);
    }
    //计算pa和pb的夹角
    double rad(Point a,Point b){
        Point p = *this;
        return fabs(atan2(fabs((a - p) ^ (b - p)),(a - p) * (b - p)));
    }
    //化为长度为r的向量
    Point turn(double r){
        double l = len();
        if(!sgn(l)) return *this;//如果原来长度为0,直接返回这个点
        r /= l;
        return Point(x * r,y * r);
    }
    //逆时针旋转90
    Point rotleft(){
        return Point(-y,x);
    }
    //顺时针旋转90
    Point rotright(){
        return Point(y,-x);
    }
    //绕p点逆时针旋转angle
    Point rot(Point p,double angle){
        Point v = (*this) - p;
        double c = cos(angle), s = sin(angle);
        return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
    }
};

关于直线的公式

仍然是定义直线的结构体,两个点来存直线,因此直线中的计算依附于上面我们已经写了的点的运算

//用两点来存
struct Line{
    Point s,e;
    Line(){}
    Line(Point _s,Point _e){
        s = _s;
        e = _e;
    }
    //根据一个点和倾斜角angle确定直线
    Line(Point p,double angle){
        s = p;
        if(sgn(angle - pi / 2) == 0){
            e = (s + Point(0,1));
        }//斜率不存在,就是一条竖线
        else{
            e = (s + Point(1,tan(angle)));//x增加1,y增加tan,由tan=y/x,当x=1,y=tan
        }
    }
    //ax+by+c=0
    Line(double a,double b,double c){
        if(sgn(a) == 0){//斜率为0,一条横线
            s = Point(0,-c / b);
            e = Point(1,-c / b);
        }
        else if(sgn(b) == 0){//斜率不存在,一条横线
            s = Point(-c / a,0);
            e = Point(-c / a,1);
        }
        else{//令x=0算一个点,令x=1算一个点
            s = Point(0,-c / b);
            e = Point(1,(-c - a) / b);
        }
    }
    bool operator == (Line v){
        return (s == v.s) && (e == v.e);
    }
    void input(){
        s.input();
        e.input();
    }
    void adjust(){//以后让e作为较大的点
        if(e < s) swap(s,e);
    }
    //求线段长度
    double length(){
        return s.dis(e);
    }
    //返回直线倾斜角
    double angle(){
        double k = atan2(e.y - s.y, e.x - s.x);
        if(sgn(k) < 0) k += pi;//如果是负的,加上180度,得到补角因为直线倾斜角在0-180度
        if(sgn(k-pi) == 0) k -= pi;//如果是180度就变成0度
        return k;
    }
};

1.点和直线的位置关系

利用叉积巧妙解决
这里写图片描述
在左侧

s p × s e < 0

在右侧
s p × s e > 0

其他说明在线上

模板:

//点和直线关系
    //1 在左侧
    //2 在右侧
    //3 在直线上
    int relation(Point p){
        int c = sgn((p - s) ^ (e - s));
        if(c < 0) return 1;
        else if(c > 0) return 2;
        else return 3;
    }

2.点和线段的位置关系

叉积和点积的结合判断

先用叉积判断是否在直线上,如上面所说

再用点积判断是否在e,s两点之间

计算点积 s p e p 的正负

如果在线段上一定为负否则一定为正
这里写图片描述
模板:

//点在线段上的判断
    bool pointonseg(Point p){
        return sgn((p - s) ^ (e - s)) == 0 && sgn((p - s) * (p - e)) <= 0;//前面判断在直线上,后面判断在两点间
    }

3.点在射线上

同理先判断在直线上然后用点积

s p s e
的正负判断

大于等于0说明在射线上
这里写图片描述
模板:

    //点在射线上的判断
    bool pointonray(Point p){
        return sgn((p - s) ^ (e - s)) == 0 && sgn((p - s) * (e - s)) >= 0;
    }

4.两线段的相交判断

首先需要了解几个概念

线段的规范相交: 交点在线段内部(不在任何一条线段的端点上).

线段的非规范相交: 交点在某条线段的端点.

其他情况就是不相交了

首先对于两条线段,先利用叉积判断是否相交
这里写图片描述
先按照图1,利用叉积看另一条线段点是否在这条线段的两侧

s e × s v . s

s e × s v . e

如果线段 v . s v . e 两端点在线段 s e 则两个叉积的符号相反

同理在按照图2,看是否线段端点都相互在另外一条线段两侧,如果成立就是规范相交

之后判断是否非规范相交,即有一个端点是交点

我们只拿一个例子来说明如图
这里写图片描述
这种情况是叉积

s e × s v . s = 0
的情况

此时这种情况说明点v.s在在se所在直线上,我们只需要判断点v.s在线段se上就行,判断点在线段上的模板上面说了,其他四个点同理

模板:

    //两线段相交判断
    //2 规范相交
    //1 非规范相交
    //0 不相交
    int segcrossseg(Line v){
        int d1 = sgn((e - s) ^ (v.s - s));
        int d2 = sgn((e - s) ^ (v.e - s));
        int d3 = sgn((v.e - v.s) ^ (s - v.s));
        int d4 = sgn((v.e - v.s) ^ (e - v.s));
        if((d1 ^ d2) == -2 && (d3 ^ d4) == -2) return 2;//1^-1 = -2
        return (d1 == 0 && sgn((v.s - s) * (v.s - e)) <= 0) ||
            (d2 == 0 && sgn((v.e - s) * (v.e - e)) <= 0) ||
            (d3 == 0 && sgn((s - v.s) * (s - v.e)) <= 0) ||
            (d4 == 0 && sgn((e - v.s) * (e - v.e)) <= 0);
    }

5.直线和线段的相交判断

这个更加容易,判断条件减少,只需要判断线段两端点在直线两侧就行了,以为直线无限延伸一定可以相交

模板:

    //直线和线段相交判断
    //-*this line -v seg
    //2 规范相交
    //1 非规范相交
    //0 不相交
    int linecrossseg(Line v){
        int d1 = sgn((e - s) * (v.s - s));
        int d2 = sgn((e - s) * (v.e - s));
        if((d1 ^ d2) == -2) return 2;
        return (d1 == 0 || d2 == 0);
    }

7.两向量平行

直接判断叉积为0
模板:

//两向量平行
    bool parallel(Line v){
        return sgn((e - s) ^ (v.e - v.s)) == 0
    }

8.两直线关系

模板:

    //两直线关系
    //0 平行
    //1 重合
    //2 相交
    int linecrossline(Line v){
        if((*this).parallel(v))
            return v.relation(s) == 3;
        return 2;
    }

9.求两直线交点

一般来求两个支线的交点我们可以通过直线方程联立,但是那样写起来很麻烦,代码冗长,我们希望有一个通用的公式可以直接求解

下面介绍一种利用叉积计算交点的方法
这里写图片描述
首先先发出一个公式叫定比分点公式,这是高中的公式,可能给名字不太熟悉,但是下面给出公式一定是见过的,下面只给出坐标公式:

在平面直角坐标系内,已知两点 A ( x 1 , y 1 ) , B ( x 2 , y 2 ) 在两点连线上有一点P,设它的坐标为 ( x , y ) A P : P B = λ 那么我们说P分有向线段 A B 的比为 λ 我们将

A P P B = λ

x = x 1 + λ x 2 1 + λ

y = y 1 + λ y 2 1 + λ

当P为内分点时, λ > 0 ;当P为外分点时, λ < 0 ( λ 1 ) ;当P与A重合时, λ > 0 ;当P与B重合时, λ 不存在

注意上面的公式说的是求有向线段的,而我们求得是直线的那么一定是内分点,所以 λ 一定是正的

根据上面的图我们发现AP和BP的比值实际上就是绿色高的比值,其实是就是黄色三角形和蓝色三角形的面积比值,而面积比值等同于叉积的比值,所以我们有:

| A P | | B P | = S Δ A D C S Δ B D C = | D C × D A | | D C × D B |

a 1 = D C × D A

a 2 = D C × D B

比值

λ = a 1 a 2

注意因为我们求叉积的顺序,使得当前比值为负数即( λ < 0 )

所以将原来的加号变成减号就好了

x = x 1 λ x 2 1 λ = x 1 a 1 a 2 x 2 1 a 1 a 2 = a 2 x 1 a 1 x 2 a 2 a 1

y = y 1 λ y 2 1 λ = y 1 a 1 a 2 y 2 1 a 1 a 2 = a 2 y 1 a 1 y 2 a 2 a 1

模板:

    //求两直线的交点
    //要保证两直线不平行不重合
    Point crosspoint(Line v){
        double a1 = (v.e - v.s) ^ (s - v.s);
        double a2 = (v.e - v.s) ^ (e - v.s);
        return Point((s.x * a2 - e.x * a1) / (a2 - a1),(s.y * a2 - e.y * a1) / (a2 - a1));
    }

10.求点到直线的距离

这里写图片描述
根据图片很容易看出,只需要求出叉积 | s p × s p | 然后除去se的长度就行了
模板:

    //点到直线的距离
    double dispointtoline(Point p){
        return fabs((p - s) ^ (e - s)) / length();
    }

11.点到线段的距离

先看能否向线段作垂线,能就和上面方法,一样,不能就是点到线段端点的距离

判断能否作垂线就是用点积看点p是否在两点之间
模板:

    //点到线段的距离
    double dispointtoseg(Point p){
        if(sgn((p - s) * (e - s)) < 0 || sgn((p - e) * (s - e)) < 0)
            return min(p.dis(s),p.dis(e));
        return dispointtoline(p);
    }

12.线段到线段的距离

    //返回线段到线段的距离
    //前提是两线段不相交,相交距离就是0了
    double dissegtoseg(Line v){
        return min(min(dispointtoseg(v.s),dispointtoseg(v.e)),min(v.dispointtoseg(s),v.dispointtoseg(e)));
    }

13.点到直线上的投影

这里写图片描述
给点s的坐标加上长度为 | s p | 的向量 s e 即可

因此目标求长度为|sp’|的向量 s e

s e s p | s e | 2

= | s e | | s p | c o s α | s e | | s e |

= | s p | c o s α | s e |

这就是比值,即点s的坐标应该加的长度
模板:

    //返回点在直线上的投影
    Point lineprog(Point p){
        return s + (((e - s) * ((e - s) * (p - s))) / ((e - s).len2()));
    }

14.点p关于直线的对称点

首先根据上面的式子得到投影,然后用中点坐标公式求出对称点

中点坐标公式:

已知两点 A ( x 1 , y 1 ) , B ( x 2 , y 2 ) ,设两点的中点为 P ( x , y ) 中点公式为:

x = x 1 + x 2 2 y = y 1 + y 2 2

所以知道中点和一个点可以求另一个点

x 2 = 2 x x 1 y 2 = 2 y y 1

模板:

    //返回点p关于直线的对称点
    Point symmetrypoint(Point p){
        Point q = lineprog(p);
        return Point(2 * q.x - p.x, 2 * q.y - p.y);
    }

以上便是关于直线的所有公式,下面是关于直线代码汇总(注意只是直线的汇总,里面用到点的一些方法,因此代码想跑需要和上面点的代码放一起才行,最后我会放上所有代码汇总可以运行的)
模板:

//用两点来存
struct Line{
    Point s,e;
    Line(){}
    Line(Point _s,Point _e){
        s = _s;
        e = _e;
    }
    //根据一个点和倾斜角angle确定直线
    Line(Point p,double angle){
        s = p;
        if(sgn(angle - pi / 2) == 0){
            e = (s + Point(0,1));
        }//斜率不存在,就是一条竖线
        else{
            e = (s + Point(1,tan(angle)));//x增加1,y增加tan,由tan=y/x,当x=1,y=tan
        }
    }
    //ax+by+c=0
    Line(double a,double b,double c){
        if(sgn(a) == 0){//斜率为0,一条横线
            s = Point(0,-c / b);
            e = Point(1,-c / b);
        }
        else if(sgn(b) == 0){//斜率不存在,一条横线
            s = Point(-c / a,0);
            e = Point(-c / a,1);
        }
        else{//令x=0算一个点,令x=1算一个点
            s = Point(0,-c / b);
            e = Point(1,(-c - a) / b);
        }
    }
    bool operator == (Line v){
        return (s == v.s) && (e == v.e);
    }
    void input(){
        s.input();
        e.input();
    }
    void adjust(){
        if(e < s) swap(s,e);
    }
    //求线段长度
    double length(){
        return s.dis(e);
    }
    //返回直线倾斜角
    double angle(){
        double k = atan2(e.y - s.y, e.x - s.x);
        if(sgn(k) < 0) k += pi;
        if(sgn(k-pi) == 0) k -= pi;
        return k;
    }
    //点和直线关系
    //1 在左侧
    //2 在右侧
    //3 在直线上
    int relation(Point p){
        int c = sgn((p - s) ^ (e - s));
        if(c < 0) return 1;
        else if(c > 0) return 2;
        else return 3;
    }
    //点在线段上的判断
    bool pointonseg(Point p){
        return sgn((p - s) ^ (e - s)) == 0 && sgn((p - s) * (p - e)) <= 0;//前面判断在直线上,后面判断在两点间
    }
    //点在射线上的判断
    bool pointonray(Point p){
        return sgn((p - s) ^ (e - s)) == 0 && sgn((p - s) * (e - s)) >= 0;
    }
    //两向量平行
    bool parallel(Line v){
        return sgn((e - s) ^ (v.e - v.s)) == 0;
    }
    //两线段相交判断
    //2 规范相交
    //1 非规范相交
    //0 不相交
    int segcrossseg(Line v){
        int d1 = sgn((e - s) ^ (v.s - s));
        int d2 = sgn((e - s) ^ (v.e - s));
        int d3 = sgn((v.e - v.s) ^ (s - v.s));
        int d4 = sgn((v.e - v.s) ^ (e - v.s));
        if((d1 ^ d2) == -2 && (d3 ^ d4) == -2) return 2;//1^-1 = -2
        return (d1 == 0 && sgn((v.s - s) * (v.s - e)) <= 0) ||
            (d2 == 0 && sgn((v.e - s) * (v.e - e)) <= 0) ||
            (d3 == 0 && sgn((s - v.s) * (s - v.e)) <= 0) ||
            (d4 == 0 && sgn((e - v.s) * (e - v.e)) <= 0);
    }
    //直线和线段相交判断
    //-*this line -v seg
    //2 规范相交
    //1 非规范相交
    //0 不相交
    int linecrossseg(Line v){
        int d1 = sgn((e - s) * (v.s - s));
        int d2 = sgn((e - s) * (v.e - s));
        if((d1 ^ d2) == -2) return 2;
        return (d1 == 0 || d2 == 0);
    }
    //两直线关系
    //0 平行
    //1 重合
    //2 相交
    int linecrossline(Line v){
        if((*this).parallel(v))
            return v.relation(s) == 3;
        return 2;
    }
    //求两直线的交点
    //要保证两直线不平行不重合
    Point crosspoint(Line v){
        double a1 = (v.e - v.s) ^ (s - v.s);
        double a2 = (v.e - v.s) ^ (e - v.s);
        return Point((s.x * a2 - e.x * a1) / (a2 - a1),(s.y * a2 - e.y * a1) / (a2 - a1));
    }
    //点到直线的距离
    double dispointtoline(Point p){
        return fabs((p - s) ^ (e - s)) / length();
    }
    //点到线段的距离
    double dispointtoseg(Point p){
        if(sgn((p - s) * (e - s)) < 0 || sgn((p - e) * (s - e)) < 0)
            return min(p.dis(s),p.dis(e));
        return dispointtoline(p);
    }
    //返回线段到线段的距离
    //前提是两线段不相交,相交距离就是0了
    double dissegtoseg(Line v){
        return min(min(dispointtoseg(v.s),dispointtoseg(v.e)),min(v.dispointtoseg(s),v.dispointtoseg(e)));
    }
    //返回点p在直线上的投影
    Point lineprog(Point p){
        return s + (((e - s) * ((e - s) * (p - s))) / ((e - s).len2()));
    }
    //返回点p关于直线的对称点
    Point symmetrypoint(Point p){
        Point q = lineprog(p);
        return Point(2 * q.x - p.x, 2 * q.y - p.y);
    }
};

三、关于圆的公式

首先还是圆结构体的基本定义和基本操作

struct circle{
    Point p;//圆心
    double r;//半径
    circle(){}
    circle(Point _p,double _r){
        p = _p;
        r = _r;
    }
    circle(double x,double y,double _r){
        p = Point(x,y);
        r = _r;
    }
    void input(){
        p.input();
        scanf("%lf",&r);
    }
    void output(){
        printf("%.2f %.2f %.2f\n",p.x,p.y,r);
    }
    bool operator == (circle v){
        return (p == v.p) && sgn(r - v.r) == 0;
    }
    bool operator < (circle v)const{
        return ((p < v.p) || (p == v.p && sgn(r-v.r) < 0));
    }
    double area(){
        return pi * r * r;
    }
    double circumference(){
        return 2.0 * pi * r;
    }
};

1.求三角形的外接圆和内切圆

由于这两个都是求圆,因此我们都把它们写成构造函数,为了进行区分,我们给求内切圆的构造函数多加一个缺省参数来进行区别
求三角形外接圆的方式是通过求出两条三角形的中垂线,然后求出交点p为圆心,半径为pa的长度
中垂线可以通过中点坐标求中点,然后对这条边左旋90度得到垂直的向量,然后以中点为起点即可
这些方法上面已经说过了,如果你前面已经认真看了,那么这里画画图应该很好理解
模板:

    //三角形的外接圆,利用两条边的中垂线得到圆心
    circle(Point a,Point b,Point c){
        Line u = Line((a + b) / 2,((a + b) / 2) + ((b - a).rotleft()));
        Line v = Line((b + c) / 2,((b + c) / 2) + ((c - b).rotleft()));
        p = u.crosspoint(v);
        r = p.dis(a);
    }

对于内切圆来说方法和上面类似,只不过上面求两条中垂线变成了求两条角平分线,然后求交点就是圆心,圆心到三角形边的距离就是半径,利用点到直线的距离来求。不过我确实没有看懂,kuangbin模板上求角平分线的过程的原理,十分抱歉

模板:

    //三角形的内切圆,利用两条角平分线得到圆心
    circle(Point a,Point b,Point c,bool t){
        Line u,v;
        double m = atan2(b.y - a.y, b.x - a.x), n = atan2(c.y - a.y, c.x - a.x);
        u.s = a;
        u.e = u.s + Point(cos((n + m) / 2), sin(n + m) / 2);
        v.s = b;
        m = atan2(a.y - b.y, a.x - b.x), n = atan2(c.y - b.y, c.x - b.x);
        v.e = v.s + Point(cos((n + m) / 2),sin((n + m) / 2));
        p = u.crosspoint(v);
        r = Line(a,b).dispointtoseg(p);
    }

2.点和圆的关系

通过点到圆心的距离判断

    //点和圆的关系
    //0圆外 1圆上 2园内
    int relation(Point b){
        double dst = b.dis(p);
        if(sgn(dst - r) < 0) return 2;
        else if(sgn(dst - r) == 0) return 1;
        return 0;
    }

3.线段和圆的关系

比较的是圆心到线段的距离和半径的关系

    //线段和圆的关系
    //0 相离  1 相切  2 相交或圆内
    int relationseg(Line v){
        double dst = v.dispointtoseg(p);
        if(sgn(dst - r) < 0) return 2;
        else if(sgn(dst - r) == 0) return 1;
        return 0;
    }

4.直线和圆的关系

比较圆心到直线的距离和半径的关系

    //直线和圆的关系
    int relationline(Line v){
        double dst = v.dispointtoline(p);
        if(sgn(dst - r) < 0) return 2;
        else if(sgn(dst - r) == 0) return 1;
        return 0;
    }

5.两圆关系

判断圆心距和半径和差的关系

    //两圆关系
    //5 相离
    //4 外切
    //3 相交
    //2 内切
    //1 内含
    int relationcircle(circle v){
        double d = p.dis(v.p);
        if(sgn(d - r - v.r) > 0) return 5;
        if(sgn(d - r - v.r) == 0) return 4;
        double l = fabs(r - v.r);
        if(sgn(d - r - v.r) < 0 && sgn(d - l) > 0) return 3;
        if(sgn(d - l) == 0) return 2;
        if(sgn(d - l) < 0) return 1;
    }

6.求两圆的交点

首先根据上面判断两圆关系,如果没有交点直接返回
下面是对一般情况的讨论
如果使用方程求解将会十分麻烦,这里仍然是使用向量的办法
这里写图片描述
首先我们知道相交圆的一个性质,就是两个相交点的连线被圆心的连线垂直平分即

| p 1 k | = | p 2 k |

我们想要知道 | p 1 k | 的长度,这对我们相当重要,为什么重要呢?

只要我们知道了k点向量坐标,通过左旋90°和右旋90°,然后加上 | p 1 k | 长度就行了

首先根据余弦定理得到

c o s θ = | p   v . p | 2 + r 2 v . r 2 2 r | p   v . p |

所以

| p k | = r c o s θ = | p   v . p | 2 + r 2 v . r 2 2 | p   v . p |

根据勾股定理可得 | p 1 k | 假设记为h

怎么得到k坐标呢

首先把向量 p   v . p 化成长度为 | p k | 长度的向量,然后在p点基础上加上这个向量就行了,然后得到了k点,只需要左右旋转90°,但是现在长度仍然是 | p k | ,再化成h的长度就好了,这些函数前面都写过了

模板:

    //求两个圆的交点,返回0表示没有交点,1是一个交点,2是两个交点
    int pointcrosscircle(circle v,Point &p1,Point &p2){
        int rel = relationcircle(v);
        if(rel == 1 || rel == 5) return 0;
        double d = p.dis(v.p);//圆心距
        double l = (d * d + r * r - v.r * v.r) / (2 * d);
        double h = sqrt(r * r - l * l);
        Point tmp = p + (v.p - p).turn(l);
        p1 = tmp + ((v.p - p).rotleft().turn(h));
        p2 = tmp + ((v.p - p).rotright().turn(h));
        if(rel == 2 || rel == 4)
            return 1;
        return 2;
    }

7.求直线和圆的交点,返回交点个数

做法和上面做法类似
这里写图片描述
同样是得到a点,用特定长度的向量相加减得到点坐标
模板:

    //求直线和圆的交点,返回交点个数
    int pointcrossline(Line v,Point &p1,Point &p2){
        if(!(*this).relationline(v)) return 0;
        Point a = v.lineprog(p);
        double d = v.dispointtoline(p);
        d = sqrt(r * r - d * d);
        if(sgn(d) == 0){
            p1 = a;
            p2 = a;
            return 1;
        }
        p1 = a + (v.e - v.s).turn(d);
        p2 = a - (v.e - v.s).turn(d);
        return 2;
    }

8.得到过a,b两点,半径为r1的两个圆

emmmmmm。。。。这个没看懂求得啥
模板:

    //得到过a,b两点,半径r1的两个圆
    int getcircel(Point a,Point b,double r1,circle &c1,circle &c2){
        circle x(a,r1),y(b,r1);
        int t = x.pointcrosscircle(y,c1.p,c2.p);
        if(!t) return 0;
        c1.r = c2.r = r;
        return t;
    }

9.得到与直线u相切,过点q,半径为r1的圆

    //得到与直线u相切,过点q,半径为r1的圆
    int getcircle(Line u,Point q,double r1,circle &c1,circle &c2){
        double dis = u.dispointtoline(q);
        if(sgn(dis - r1 * 2) > 0) return 0;
        if(sgn(dis) == 0){
            c1.p = q + ((u.e - u.s).rotleft().turn(r1));
            c2.p = q + ((u.e - u.s).rotright().turn(r1));
            c1.r = c2.r = r1;
            return 2;
        }
        Line u1 = Line((u.s + (u.e - u.s).rotleft().turn(r1)),(u.e + (u.e - u.s).rotleft().turn(r1)));
        Line u2 = Line((u.s + (u.e - u.s).rotright().turn(r1)),(u.e + (u.e - u.s).rotright().turn(r1)));
        circle cc = circle(q,r1);
        Point p1,p2;
        if(!cc.pointcrossline(u1,p1,p2))
            cc.pointcrossline(u2,p1,p2);
        c1 = circle(p1,r1);
        if(p1 == p2){
            c2 = c1;
            return 1;
        }
        c2 = circle(p2,r1);
        return 2;
    }

10.11.同时与直线 u,v 相切,半径为 r1 的圆,同时与不相交圆 cx,cy 相切,半径为 r1 的圆

//同时与直线 u,v 相切,半径为 r1 的圆
    //测试:UVA12304
    int getcircle(Line u,Line v,double r1,circle &c1,circle &c2, circle &c3,circle &c4){
        if(u.parallel(v))return 0;//两直线平行
        Line u1 = Line(u.s + (u.e - u.s).rotleft().turn(r1),u.e + (u.e - u.s).rotleft().turn(r1));
        Line u2 = Line(u.s + (u.e - u.s).rotright().turn(r1),u.e + (u.e - u.s).rotright().turn(r1));
        Line v1 = Line(v.s + (v.e - v.s).rotleft().turn(r1),v.e + (v.e - v.s).rotleft().turn(r1));
        Line v2 = Line(v.s + (v.e - v.s).rotright().turn(r1),v.e + (v.e - v.s).rotright().turn(r1));
        c1.r = c2.r = c3.r = c4.r = r1;
        c1.p = u1.crosspoint(v1);
        c2.p = u1.crosspoint(v2);
        c3.p = u2.crosspoint(v1);
        c4.p = u2.crosspoint(v2);
        return 4;
    }
        //同时与不相交圆 cx,cy 相切,半径为 r1 的圆
        //测试:UVA12304
    int getcircle(circle cx,circle cy,double r1,circle &c1,circle & c2){
        circle x(cx.p,r1+cx.r),y(cy.p,r1+cy.r);
        int t = x.pointcrosscircle(y,c1.p,c2.p);
        if(!t)return 0;
        c1.r = c2.r = r1;
        return t;
    }

12.过一点作圆的切线

这里写图片描述
通过求出红色线长度和绿色线长度,利用向量旋转得到 k 1 , k 2 的坐标这样 q k ! , q k 2 两条线就得到了
先看点的位置
模板:

    //过一点作圆的切线(先判断点和圆的关系)
    int tangentline(Point q,Line &u,Line &v){
        int x = relation(q);
        if(x == 2) return 0;
        if(x == 1){
            u = Line(q, q + (q - p).rotleft());
            v = u;
            return 1;
        }
        double d = p.dis(q);
        double l = r * r / d;
        double h = sqrt(r * r - l * l);
        u = Line(q, p + ((q - p).turn(l) + (q - p).rotleft().turn(h)));
        v = Line(q, p + ((q - p).turn(l) + (q - p).rotright().turn(h)));
        return 2;
    }

13.求相交圆的面积

一般情况
这里写图片描述
通过求出扇形面积和,在减去三角形面积,最后乘2,因为上下对称
其中三角形面积可利用海伦公式求

S = p ( p a ) ( p b ) ( p c ) p = 1 2 ( a + b + c )

abc分别是三角形三条边长

相内切,面积为较小圆的面积,否则为0

    //求两圆相交的面积
    double areacircle(circle v){
        int rel = relationcircle(v);
        if(rel >= 4) return 0.0;
        if(rel <= 2) return min(area(),v.area());
        double d = p.dis(v.p);
        double hf = (r + v.r + d) / 2.0;
        double ss = 2 * sqrt(hf * (hf - r) * (hf - v.r) * (hf - d));//海伦公式求三角形面积
        double a1 = acos((r * r + d * d - v.r * v.r) / (2.0 * r * d));
        a1 = a1 * r * r;
        double a2 = acos((v.r * v.r + d * d - r * r) / (2.0 * v.r * d));
        a2 = a2 * v.r * v.r;
        return a1 + a2 - ss;
    }

14.求圆和三角形pab的相交面积

    //求圆和三角形 pab 的相交面积
    //测试:POJ3675 HDU3982 HDU2892
    double areatriangle(Point a,Point b){
        if(sgn((p - a)^(p - b)) == 0)return 0.0;
        Point q[5];
        int len = 0;
        q[len++] = a;
        Line l(a,b);
        Point p1,p2;
        if(pointcrossline(l,q[1],q[2])==2){
            if(sgn((a - q[1])*(b - q[1]))<0)q[len++] = q[1];
            if(sgn((a - q[2])*(b - q[2]))<0)q[len++] = q[2];
        }
        q[len++] = b;
        if(len == 4 && sgn((q[0] - q[1])*(q[2] - q[1]))>0) swap(q[1],q [2]);
        double res = 0;
        for(int i = 0;i < len - 1;i++){
            if(relation(q[i])==0||relation(q[i+1])==0){
                double arg = p.rad(q[i],q[i+1]);
                res += r*r*arg/2.0;
            }
           else{
                res  += fabs((q[i]-p)^(q[i+1]-p))/2.0;
            }
        }
        return res;
    }

与圆相关代码汇总

struct circle{
    Point p;//圆心
    double r;//半径
    circle(){}
    circle(Point _p,double _r){
        p = _p;
        r = _r;
    }
    circle(double x,double y,double _r){
        p = Point(x,y);
        r = _r;
    }
    //三角形的外接圆,利用两条边的中垂线得到圆心
    circle(Point a,Point b,Point c){
        Line u = Line((a + b) / 2,((a + b) / 2) + ((b - a).rotleft()));
        Line v = Line((b + c) / 2,((b + c) / 2) + ((c - b).rotleft()));
        p = u.crosspoint(v);
        r = p.dis(a);
    }
    //三角形的内切圆,利用两条角平分线得到圆心
    circle(Point a,Point b,Point c,bool t){
        Line u,v;
        double m = atan2(b.y - a.y, b.x - a.x), n = atan2(c.y - a.y, c.x - a.x);
        u.s = a;
        u.e = u.s + Point(cos((n + m) / 2), sin(n + m) / 2);
        v.s = b;
        m = atan2(a.y - b.y, a.x - b.x), n = atan2(c.y - b.y, c.x - b.x);
        v.e = v.s + Point(cos((n + m) / 2),sin((n + m) / 2));
        p = u.crosspoint(v);
        r = Line(a,b).dispointtoseg(p);
    }
    void input(){
        p.input();
        scanf("%lf",&r);
    }
    void output(){
        printf("%.2f %.2f %.2f\n",p.x,p.y,r);
    }
    bool operator == (circle v){
        return (p == v.p) && sgn(r - v.r) == 0;
    }
    bool operator < (circle v)const{
        return ((p < v.p) || (p == v.p && sgn(r-v.r) < 0));
    }
    double area(){
        return pi * r * r;
    }
    double circumference(){
        return 2.0 * pi * r;
    }
    //点和圆的关系
    //0圆外 1圆上 2园内
    int relation(Point b){
        double dst = b.dis(p);
        if(sgn(dst - r) < 0) return 2;
        else if(sgn(dst - r) == 0) return 1;
        return 0;
    }
    //线段和圆的关系
    //0 相离  1 相切  2 相交或圆内
    int relationseg(Line v){
        double dst = v.dispointtoseg(p);
        if(sgn(dst - r) < 0) return 2;
        else if(sgn(dst - r) == 0) return 1;
        return 0;
    }
    //直线和圆的关系
    int relationline(Line v){
        double dst = v.dispointtoline(p);
        if(sgn(dst - r) < 0) return 2;
        else if(sgn(dst - r) == 0) return 1;
        return 0;
    }
    //两圆关系
    //5 相离
    //4 外切
    //3 相交
    //2 内切
    //1 内含
    int relationcircle(circle v){
        double d = p.dis(v.p);
        if(sgn(d - r - v.r) > 0) return 5;
        if(sgn(d - r - v.r) == 0) return 4;
        double l = fabs(r - v.r);
        if(sgn(d - r - v.r) < 0 && sgn(d - l) > 0) return 3;
        if(sgn(d - l) == 0) return 2;
        if(sgn(d - l) < 0) return 1;
    }
    //求两个圆的交点,返回0表示没有交点,1是一个交点,2是两个交点
    int pointcrosscircle(circle v,Point &p1,Point &p2){
        int rel = relationcircle(v);
        if(rel == 1 || rel == 5) return 0;
        double d = p.dis(v.p);//圆心距
        double l = (d * d + r * r - v.r * v.r) / (2 * d);
        double h = sqrt(r * r - l * l);
        Point tmp = p + (v.p - p).turn(l);
        p1 = tmp + ((v.p - p).rotleft().turn(h));
        p2 = tmp + ((v.p - p).rotright().turn(h));
        if(rel == 2 || rel == 4)
            return 1;
        return 2;
    }
    //求直线和圆的交点,返回交点个数
    int pointcrossline(Line v,Point &p1,Point &p2){
        if(!(*this).relationline(v)) return 0;
        Point a = v.lineprog(p);
        double d = v.dispointtoline(p);
        d = sqrt(r * r - d * d);
        if(sgn(d) == 0){
            p1 = a;
            p2 = a;
            return 1;
        }
        p1 = a + (v.e - v.s).turn(d);
        p2 = a - (v.e - v.s).turn(d);
        return 2;
    }
    //得到过a,b两点,半径r1的两个圆
    int getcircle(Point a,Point b,double r1,circle &c1,circle &c2){
        circle x(a,r1),y(b,r1);
        int t = x.pointcrosscircle(y,c1.p,c2.p);
        if(!t) return 0;
        c1.r = c2.r = r;
        return t;
    }
    //得到与直线u相切,过点q,半径为r1的圆
    int getcircle(Line u,Point q,double r1,circle &c1,circle &c2){
        double dis = u.dispointtoline(q);
        if(sgn(dis - r1 * 2) > 0) return 0;
        if(sgn(dis) == 0){
            c1.p = q + ((u.e - u.s).rotleft().turn(r1));
            c2.p = q + ((u.e - u.s).rotright().turn(r1));
            c1.r = c2.r = r1;
            return 2;
        }
        Line u1 = Line((u.s + (u.e - u.s).rotleft().turn(r1)),(u.e + (u.e - u.s).rotleft().turn(r1)));
        Line u2 = Line((u.s + (u.e - u.s).rotright().turn(r1)),(u.e + (u.e - u.s).rotright().turn(r1)));
        circle cc = circle(q,r1);
        Point p1,p2;
        if(!cc.pointcrossline(u1,p1,p2))
            cc.pointcrossline(u2,p1,p2);
        c1 = circle(p1,r1);
        if(p1 == p2){
            c2 = c1;
            return 1;
        }
        c2 = circle(p2,r1);
        return 2;
    }
    //同时与直线 u,v 相切,半径为 r1 的圆
    //测试:UVA12304
    int getcircle(Line u,Line v,double r1,circle &c1,circle &c2, circle &c3,circle &c4){
        if(u.parallel(v))return 0;//两直线平行
        Line u1 = Line(u.s + (u.e - u.s).rotleft().turn(r1),u.e + (u.e - u.s).rotleft().turn(r1));
        Line u2 = Line(u.s + (u.e - u.s).rotright().turn(r1),u.e + (u.e - u.s).rotright().turn(r1));
        Line v1 = Line(v.s + (v.e - v.s).rotleft().turn(r1),v.e + (v.e - v.s).rotleft().turn(r1));
        Line v2 = Line(v.s + (v.e - v.s).rotright().turn(r1),v.e + (v.e - v.s).rotright().turn(r1));
        c1.r = c2.r = c3.r = c4.r = r1;
        c1.p = u1.crosspoint(v1);
        c2.p = u1.crosspoint(v2);
        c3.p = u2.crosspoint(v1);
        c4.p = u2.crosspoint(v2);
        return 4;
    }
        //同时与不相交圆 cx,cy 相切,半径为 r1 的圆
        //测试:UVA12304
    int getcircle(circle cx,circle cy,double r1,circle &c1,circle & c2){
        circle x(cx.p,r1+cx.r),y(cy.p,r1+cy.r);
        int t = x.pointcrosscircle(y,c1.p,c2.p);
        if(!t)return 0;
        c1.r = c2.r = r1;
        return t;
    }
    //过一点作圆的切线(先判断点和圆的关系)
    int tangentline(Point q,Line &u,Line &v){
        int x = relation(q);
        if(x == 2) return 0;
        if(x == 1){
            u = Line(q, q + (q - p).rotleft());
            v = u;
            return 1;
        }
        double d = p.dis(q);
        double l = r * r / d;
        double h = sqrt(r * r - l * l);
        u = Line(q, p + ((q - p).turn(l) + (q - p).rotleft().turn(h)));
        v = Line(q, p + ((q - p).turn(l) + (q - p).rotright().turn(h)));
        return 2;
    }
    //求两圆相交的面积
    double areacircle(circle v){
        int rel = relationcircle(v);
        if(rel >= 4) return 0.0;
        if(rel <= 2) return min(area(),v.area());
        double d = p.dis(v.p);
        double hf = (r + v.r + d) / 2.0;
        double ss = 2 * sqrt(hf * (hf - r) * (hf - v.r) * (hf - d));//海伦公式求三角形面积
        double a1 = acos((r * r + d * d - v.r * v.r) / (2.0 * r * d));
        a1 = a1 * r * r;
        double a2 = acos((v.r * v.r + d * d - r * r) / (2.0 * v.r * d));
        a2 = a2 * v.r * v.r;
        return a1 + a2 - ss;
    }

        //求圆和三角形 pab 的相交面积
    //测试:POJ3675 HDU3982 HDU2892
    double areatriangle(Point a,Point b){
        if(sgn((p - a)^(p - b)) == 0)return 0.0;
        Point q[5];
        int len = 0;
        q[len++] = a;
        Line l(a,b);
        Point p1,p2;
        if(pointcrossline(l,q[1],q[2])==2){
            if(sgn((a - q[1])*(b - q[1]))<0)q[len++] = q[1];
            if(sgn((a - q[2])*(b - q[2]))<0)q[len++] = q[2];
        }
        q[len++] = b;
        if(len == 4 && sgn((q[0] - q[1])*(q[2] - q[1]))>0) swap(q[1],q [2]);
        double res = 0;
        for(int i = 0;i < len - 1;i++){
            if(relation(q[i])==0||relation(q[i+1])==0){
                double arg = p.rad(q[i],q[i+1]);
                res += r*r*arg/2.0;
            }
           else{
                res  += fabs((q[i]-p)^(q[i+1]-p))/2.0;
            }
        }
        return res;
    }
};

所有模板代码:

#include <bits/stdc++.h>
using namespace std;
const double eps = 1e8;
const double inf = 1e20;
const double pi = acos( 1.0);//圆周率
const int maxp = 1010;

//和0比较大小的函数
int sgn(double x){
    if(fabs(x) < eps)return 0;//等于0
    if(x < 0)return -1;//小于0
    else return 1; //大于0
}

//浮点数平方
inline double sqr(double x){return x*x;}

struct Point{
    double x,y;
    Point(){}
    Point(double _x,double _y){
        x = _x;
        y = _y;
    }//构造函数
    void input(){//输入
        scanf("%lf%lf",&x,&y);
    }
    void output(){//输出
        printf("%.2f %.2f\n",x,y);//保留位数可自己控制
    }
    bool operator == (Point b)const{//横纵坐标均相等
        return sgn(x - b.x) == 0 && sgn(y - b.y) == 0;
    }
    bool operator < (Point b)const{//先比横坐标,相同在比较纵坐标
        return sgn(x - b.x) == 0 ? sgn(y - b.y) < 0 : x < b.x;
    }
    Point operator - (const Point &b)const{
        return Point(x - b.x, y - b.y);
    }
    Point operator + (const Point &b)const{
        return Point(x + b.x, y + b.y);
    }
    //叉积
    double operator ^ (const Point &b)const{
        return x * b.y - y * b.x;
    }
    //点积
    double operator * (const Point &b)const{
        return x * b.x + y * b.y;
    }
    //数乘
    Point operator * (const double &k)const{
        return Point(x * k, y * k);
    }
    Point operator / (const double &k)const{
        return Point(x / k, y / k);
    }
    //返回长度
    double len(){
        return hypot(x,y);//<math.h>中函数,给定直角三角形两直角边求出斜边长度
    }
    //返回长度平方
    double len2(){
        return x * x + y * y;
    }
    //返回两点距离
    double dis(Point p){
        return hypot(x - p.x, y - p.y);
    }
    //计算pa和pb的夹角
    double rad(Point a,Point b){
        Point p = *this;
        return fabs(atan2(fabs((a - p) ^ (b - p)),(a - p) * (b - p)));
    }
    //化为长度为r的向量
    Point turn(double r){
        double l = len();
        if(!sgn(l)) return *this;//如果原来长度为0,直接返回这个点
        r /= l;
        return Point(x * r,y * r);
    }
    //逆时针旋转90
    Point rotleft(){
        return Point(-y,x);
    }
    //顺时针旋转90
    Point rotright(){
        return Point(y,-x);
    }
    //绕p点逆时针旋转angle
    Point rot(Point p,double angle){
        Point v = (*this) - p;
        double c = cos(angle), s = sin(angle);
        return Point(p.x + v.x * c - v.y * s, p.y + v.x * s + v.y * c);
    }
};
//用两点来存
struct Line{
    Point s,e;
    Line(){}
    Line(Point _s,Point _e){
        s = _s;
        e = _e;
    }
    //根据一个点和倾斜角angle确定直线
    Line(Point p,double angle){
        s = p;
        if(sgn(angle - pi / 2) == 0){
            e = (s + Point(0,1));
        }//斜率不存在,就是一条竖线
        else{
            e = (s + Point(1,tan(angle)));//x增加1,y增加tan,由tan=y/x,当x=1,y=tan
        }
    }
    //ax+by+c=0
    Line(double a,double b,double c){
        if(sgn(a) == 0){//斜率为0,一条横线
            s = Point(0,-c / b);
            e = Point(1,-c / b);
        }
        else if(sgn(b) == 0){//斜率不存在,一条横线
            s = Point(-c / a,0);
            e = Point(-c / a,1);
        }
        else{//令x=0算一个点,令x=1算一个点
            s = Point(0,-c / b);
            e = Point(1,(-c - a) / b);
        }
    }
    bool operator == (Line v){
        return (s == v.s) && (e == v.e);
    }
    void input(){
        s.input();
        e.input();
    }
    void adjust(){
        if(e < s) swap(s,e);
    }
    //求线段长度
    double length(){
        return s.dis(e);
    }
    //返回直线倾斜角
    double angle(){
        double k = atan2(e.y - s.y, e.x - s.x);
        if(sgn(k) < 0) k += pi;
        if(sgn(k-pi) == 0) k -= pi;
        return k;
    }
    //点和直线关系
    //1 在左侧
    //2 在右侧
    //3 在直线上
    int relation(Point p){
        int c = sgn((p - s) ^ (e - s));
        if(c < 0) return 1;
        else if(c > 0) return 2;
        else return 3;
    }
    //点在线段上的判断
    bool pointonseg(Point p){
        return sgn((p - s) ^ (e - s)) == 0 && sgn((p - s) * (p - e)) <= 0;//前面判断在直线上,后面判断在两点间
    }
    //点在射线上的判断
    bool pointonray(Point p){
        return sgn((p - s) ^ (e - s)) == 0 && sgn((p - s) * (e - s)) >= 0;
    }
    //两向量平行
    bool parallel(Line v){
        return sgn((e - s) ^ (v.e - v.s)) == 0;
    }
    //两线段相交判断
    //2 规范相交
    //1 非规范相交
    //0 不相交
    int segcrossseg(Line v){
        int d1 = sgn((e - s) ^ (v.s - s));
        int d2 = sgn((e - s) ^ (v.e - s));
        int d3 = sgn((v.e - v.s) ^ (s - v.s));
        int d4 = sgn((v.e - v.s) ^ (e - v.s));
        if((d1 ^ d2) == -2 && (d3 ^ d4) == -2) return 2;//1^-1 = -2
        return (d1 == 0 && sgn((v.s - s) * (v.s - e)) <= 0) ||
            (d2 == 0 && sgn((v.e - s) * (v.e - e)) <= 0) ||
            (d3 == 0 && sgn((s - v.s) * (s - v.e)) <= 0) ||
            (d4 == 0 && sgn((e - v.s) * (e - v.e)) <= 0);
    }
    //直线和线段相交判断
    //-*this line -v seg
    //2 规范相交
    //1 非规范相交
    //0 不相交
    int linecrossseg(Line v){
        int d1 = sgn((e - s) * (v.s - s));
        int d2 = sgn((e - s) * (v.e - s));
        if((d1 ^ d2) == -2) return 2;
        return (d1 == 0 || d2 == 0);
    }
    //两直线关系
    //0 平行
    //1 重合
    //2 相交
    int linecrossline(Line v){
        if((*this).parallel(v))
            return v.relation(s) == 3;
        return 2;
    }
    //求两直线的交点
    //要保证两直线不平行不重合
    Point crosspoint(Line v){
        double a1 = (v.e - v.s) ^ (s - v.s);
        double a2 = (v.e - v.s) ^ (e - v.s);
        return Point((s.x * a2 - e.x * a1) / (a2 - a1),(s.y * a2 - e.y * a1) / (a2 - a1));
    }
    //点到直线的距离
    double dispointtoline(Point p){
        return fabs((p - s) ^ (e - s)) / length();
    }
    //点到线段的距离
    double dispointtoseg(Point p){
        if(sgn((p - s) * (e - s)) < 0 || sgn((p - e) * (s - e)) < 0)
            return min(p.dis(s),p.dis(e));
        return dispointtoline(p);
    }
    //返回线段到线段的距离
    //前提是两线段不相交,相交距离就是0了
    double dissegtoseg(Line v){
        return min(min(dispointtoseg(v.s),dispointtoseg(v.e)),min(v.dispointtoseg(s),v.dispointtoseg(e)));
    }
    //返回点p在直线上的投影
    Point lineprog(Point p){
        return s + (((e - s) * ((e - s) * (p - s))) / ((e - s).len2()));
    }
    //返回点p关于直线的对称点
    Point symmetrypoint(Point p){
        Point q = lineprog(p);
        return Point(2 * q.x - p.x, 2 * q.y - p.y);
    }
};

struct circle{
    Point p;//圆心
    double r;//半径
    circle(){}
    circle(Point _p,double _r){
        p = _p;
        r = _r;
    }
    circle(double x,double y,double _r){
        p = Point(x,y);
        r = _r;
    }
    //三角形的外接圆,利用两条边的中垂线得到圆心
    circle(Point a,Point b,Point c){
        Line u = Line((a + b) / 2,((a + b) / 2) + ((b - a).rotleft()));
        Line v = Line((b + c) / 2,((b + c) / 2) + ((c - b).rotleft()));
        p = u.crosspoint(v);
        r = p.dis(a);
    }
    //三角形的内切圆,利用两条角平分线得到圆心
    circle(Point a,Point b,Point c,bool t){
        Line u,v;
        double m = atan2(b.y - a.y, b.x - a.x), n = atan2(c.y - a.y, c.x - a.x);
        u.s = a;
        u.e = u.s + Point(cos((n + m) / 2), sin(n + m) / 2);
        v.s = b;
        m = atan2(a.y - b.y, a.x - b.x), n = atan2(c.y - b.y, c.x - b.x);
        v.e = v.s + Point(cos((n + m) / 2),sin((n + m) / 2));
        p = u.crosspoint(v);
        r = Line(a,b).dispointtoseg(p);
    }
    void input(){
        p.input();
        scanf("%lf",&r);
    }
    void output(){
        printf("%.2f %.2f %.2f\n",p.x,p.y,r);
    }
    bool operator == (circle v){
        return (p == v.p) && sgn(r - v.r) == 0;
    }
    bool operator < (circle v)const{
        return ((p < v.p) || (p == v.p && sgn(r-v.r) < 0));
    }
    double area(){
        return pi * r * r;
    }
    double circumference(){
        return 2.0 * pi * r;
    }
    //点和圆的关系
    //0圆外 1圆上 2园内
    int relation(Point b){
        double dst = b.dis(p);
        if(sgn(dst - r) < 0) return 2;
        else if(sgn(dst - r) == 0) return 1;
        return 0;
    }
    //线段和圆的关系
    //0 相离  1 相切  2 相交或圆内
    int relationseg(Line v){
        double dst = v.dispointtoseg(p);
        if(sgn(dst - r) < 0) return 2;
        else if(sgn(dst - r) == 0) return 1;
        return 0;
    }
    //直线和圆的关系
    int relationline(Line v){
        double dst = v.dispointtoline(p);
        if(sgn(dst - r) < 0) return 2;
        else if(sgn(dst - r) == 0) return 1;
        return 0;
    }
    //两圆关系
    //5 相离
    //4 外切
    //3 相交
    //2 内切
    //1 内含
    int relationcircle(circle v){
        double d = p.dis(v.p);
        if(sgn(d - r - v.r) > 0) return 5;
        if(sgn(d - r - v.r) == 0) return 4;
        double l = fabs(r - v.r);
        if(sgn(d - r - v.r) < 0 && sgn(d - l) > 0) return 3;
        if(sgn(d - l) == 0) return 2;
        if(sgn(d - l) < 0) return 1;
    }
    //求两个圆的交点,返回0表示没有交点,1是一个交点,2是两个交点
    int pointcrosscircle(circle v,Point &p1,Point &p2){
        int rel = relationcircle(v);
        if(rel == 1 || rel == 5) return 0;
        double d = p.dis(v.p);//圆心距
        double l = (d * d + r * r - v.r * v.r) / (2 * d);
        double h = sqrt(r * r - l * l);
        Point tmp = p + (v.p - p).turn(l);
        p1 = tmp + ((v.p - p).rotleft().turn(h));
        p2 = tmp + ((v.p - p).rotright().turn(h));
        if(rel == 2 || rel == 4)
            return 1;
        return 2;
    }
    //求直线和圆的交点,返回交点个数
    int pointcrossline(Line v,Point &p1,Point &p2){
        if(!(*this).relationline(v)) return 0;
        Point a = v.lineprog(p);
        double d = v.dispointtoline(p);
        d = sqrt(r * r - d * d);
        if(sgn(d) == 0){
            p1 = a;
            p2 = a;
            return 1;
        }
        p1 = a + (v.e - v.s).turn(d);
        p2 = a - (v.e - v.s).turn(d);
        return 2;
    }
    //得到过a,b两点,半径r1的两个圆
    int getcircle(Point a,Point b,double r1,circle &c1,circle &c2){
        circle x(a,r1),y(b,r1);
        int t = x.pointcrosscircle(y,c1.p,c2.p);
        if(!t) return 0;
        c1.r = c2.r = r;
        return t;
    }
    //得到与直线u相切,过点q,半径为r1的圆
    int getcircle(Line u,Point q,double r1,circle &c1,circle &c2){
        double dis = u.dispointtoline(q);
        if(sgn(dis - r1 * 2) > 0) return 0;
        if(sgn(dis) == 0){
            c1.p = q + ((u.e - u.s).rotleft().turn(r1));
            c2.p = q + ((u.e - u.s).rotright().turn(r1));
            c1.r = c2.r = r1;
            return 2;
        }
        Line u1 = Line((u.s + (u.e - u.s).rotleft().turn(r1)),(u.e + (u.e - u.s).rotleft().turn(r1)));
        Line u2 = Line((u.s + (u.e - u.s).rotright().turn(r1)),(u.e + (u.e - u.s).rotright().turn(r1)));
        circle cc = circle(q,r1);
        Point p1,p2;
        if(!cc.pointcrossline(u1,p1,p2))
            cc.pointcrossline(u2,p1,p2);
        c1 = circle(p1,r1);
        if(p1 == p2){
            c2 = c1;
            return 1;
        }
        c2 = circle(p2,r1);
        return 2;
    }
    //同时与直线 u,v 相切,半径为 r1 的圆
    //测试:UVA12304
    int getcircle(Line u,Line v,double r1,circle &c1,circle &c2, circle &c3,circle &c4){
        if(u.parallel(v))return 0;//两直线平行
        Line u1 = Line(u.s + (u.e - u.s).rotleft().turn(r1),u.e + (u.e - u.s).rotleft().turn(r1));
        Line u2 = Line(u.s + (u.e - u.s).rotright().turn(r1),u.e + (u.e - u.s).rotright().turn(r1));
        Line v1 = Line(v.s + (v.e - v.s).rotleft().turn(r1),v.e + (v.e - v.s).rotleft().turn(r1));
        Line v2 = Line(v.s + (v.e - v.s).rotright().turn(r1),v.e + (v.e - v.s).rotright().turn(r1));
        c1.r = c2.r = c3.r = c4.r = r1;
        c1.p = u1.crosspoint(v1);
        c2.p = u1.crosspoint(v2);
        c3.p = u2.crosspoint(v1);
        c4.p = u2.crosspoint(v2);
        return 4;
    }
        //同时与不相交圆 cx,cy 相切,半径为 r1 的圆
        //测试:UVA12304
    int getcircle(circle cx,circle cy,double r1,circle &c1,circle & c2){
        circle x(cx.p,r1+cx.r),y(cy.p,r1+cy.r);
        int t = x.pointcrosscircle(y,c1.p,c2.p);
        if(!t)return 0;
        c1.r = c2.r = r1;
        return t;
    }
    //过一点作圆的切线(先判断点和圆的关系)
    int tangentline(Point q,Line &u,Line &v){
        int x = relation(q);
        if(x == 2) return 0;
        if(x == 1){
            u = Line(q, q + (q - p).rotleft());
            v = u;
            return 1;
        }
        double d = p.dis(q);
        double l = r * r / d;
        double h = sqrt(r * r - l * l);
        u = Line(q, p + ((q - p).turn(l) + (q - p).rotleft().turn(h)));
        v = Line(q, p + ((q - p).turn(l) + (q - p).rotright().turn(h)));
        return 2;
    }
    //求两圆相交的面积
    double areacircle(circle v){
        int rel = relationcircle(v);
        if(rel >= 4) return 0.0;
        if(rel <= 2) return min(area(),v.area());
        double d = p.dis(v.p);
        double hf = (r + v.r + d) / 2.0;
        double ss = 2 * sqrt(hf * (hf - r) * (hf - v.r) * (hf - d));//海伦公式求三角形面积
        double a1 = acos((r * r + d * d - v.r * v.r) / (2.0 * r * d));
        a1 = a1 * r * r;
        double a2 = acos((v.r * v.r + d * d - r * r) / (2.0 * v.r * d));
        a2 = a2 * v.r * v.r;
        return a1 + a2 - ss;
    }

        //求圆和三角形 pab 的相交面积
    //测试:POJ3675 HDU3982 HDU2892
    double areatriangle(Point a,Point b){
        if(sgn((p - a)^(p - b)) == 0)return 0.0;
        Point q[5];
        int len = 0;
        q[len++] = a;
        Line l(a,b);
        Point p1,p2;
        if(pointcrossline(l,q[1],q[2])==2){
            if(sgn((a - q[1])*(b - q[1]))<0)q[len++] = q[1];
            if(sgn((a - q[2])*(b - q[2]))<0)q[len++] = q[2];
        }
        q[len++] = b;
        if(len == 4 && sgn((q[0] - q[1])*(q[2] - q[1]))>0) swap(q[1],q [2]);
        double res = 0;
        for(int i = 0;i < len - 1;i++){
            if(relation(q[i])==0||relation(q[i+1])==0){
                double arg = p.rad(q[i],q[i+1]);
                res += r*r*arg/2.0;
            }
           else{
                res  += fabs((q[i]-p)^(q[i+1]-p))/2.0;
            }
        }
        return res;
    }
};
int main(){

    return 0;
}

猜你喜欢

转载自blog.csdn.net/codeswarrior/article/details/82414781