2013-team5/geo2d

从 Trac 迁移的文章

这是从旧校内 Wiki 迁移的文章,可能存在一些样式问题,您可以向 memset0 反馈。

原文章内容如下:

{{{
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 1000;
const double eps = 1e-8, PI = atan2(0, -1);
const double INF=1e10;
inline double sqr(double x){ return x * x; }
inline bool zero(double x){
    return (x > 0 ? x : -x) < eps;
}
inline int sgn(double x){
    return (x > eps ? 1 : (x + eps < 0 ? -1 : 0));
}
struct point{
    double x, y;
    point(double x, double y):x(x), y(y) {}
    point() {}
    bool operator == (const point & a) const{
        return sgn(x - a.x) == 0 && sgn(y - a.y) == 0;
        }
    bool operator != (const point & a) const{
        return sgn(x - a.x) != 0 || sgn(y - a.y) != 0;
        }
    bool operator  < (const point & a) const{
        return sgn(x - a.x) < 0 || sgn(x - a.x) == 0 && sgn(y - a.y) < 0;
        }
    point operator + (const point & a) const{
        return point(x + a.x, y + a.y);
        }
    point operator - (const point & a) const{
        return point(x - a.x, y - a.y);
        }
    point operator * (const double & a) const{
        return point(x * a, y * a);
        }
    point operator / (const double & a) const{
        return point(x / a, y / a);
        }
    double operator * (const point & a) const{
        return x * a.y - y * a.x;
        }//xmult
    double operator ^ (const point & a) const{
        return x * a.x + y * a.y;
        }//dmult
    double sqrlen() const{ return sqr(x) + sqr(y); }
    double length() const{ return sqrt(sqrlen()); }
    point trunc(double a) const{
        return (*this) * (a / length());
        }
    point rotate(double ang) const{
        point p(sin(ang), cos(ang));
        return point((*this) * p, (*this) ^ p);
        }
    point rotate(const point & a) const{
        point p(-a.y, a.x); p = p.trunc(1.0);
        return point((*this) * p, (*this) ^ p);
        }
};

struct line{
    point s,t;
    double ang;
    line() {}
    line(point s,point t):s(s), t(t){ ang=atan2(t.y-s.y, t.x-s.x); }
    bool operator < (const line &l) const { return ang<l.ang; }
};

typedef vector <point> VP;


/***************** Part one: Dot&Line *****************/


inline bool DotOnSeg(point &p, point &l1, point &l2){
    return zero((l2-l1)*(p-l1)) && sgn((l1-p)^(l2-p))<0;
}  /* 判断点是否在线段上(不含端点);若包含线段端点,需将<0改成<=0*/ /* tested */

inline double DotToSeg(point &p, point &l1, point &l2){
    point L=l2-l1;
    if (sgn((p-l1)^L)<=0) return (p-l1).length();/* 1 */
    if (sgn((p-l2)^L)> 0) return (p-l2).length();/* 2 */
    return fabs(L*(p-l1)/L.length());
}   /* 返回点到线段距离;去掉2为点到射线距离;去掉1,2为点到直线距离 */ /* tested */

inline double DotToLine(point &p, point &l1, point &l2){
    point L=l2-l1; return fabs(L*(p-l1)/L.length());
}   /* 返回点到直线距离 */ /* tested */

inline point DotProLine(point &p, point &l1, point &l2){
    point L=l2-l1;
    return l1+L*((p-l1)^L)/L.sqrlen();
}   /* 返回点在直线上的投影点 */ /* tested */

inline int SideLD(point l1, point l2, point p){
    return sgn((l2-l1)*(p-l1));
}   /* 判断点在有向直线<l1l2>的哪测,1为左侧,0在线上,-1在右侧 */

inline bool Parallal(point &u1, point &u2, point &v1, point &v2){
    return zero((u2-u1)*(v2-v1));
}   /* 判断两直线,射线,线段是否平行 */ /* tested */

inline bool SegAcross(point &u1, point &u2, point &v1, point &v2){
    return sgn(((u2-u1)*(v1-u1))*((u2-u1)*(v2-u1)))<0
        && sgn(((v2-v1)*(u1-v1))*((v2-v1)*(u2-v1)))<0;
}   /* 判断两线段是否规范相交(不包括端点) */

inline point Intersection(point &u1, point &u2, point &v1, point &v2){
    double t=((u2-u1)*(v1-u1))/((v2-v1)*(u2-u1));
    return v1+(v2-v1)*t;
}   /* 使用前需保证两直线,射线或者线段相交,返回其交点 */ /* tested */


/***************** Part two: Polygon   *****************/


inline bool DotInsidePoly(point &p, point *v, int n){
    int cnt=0;
    point t=p; t.y=INF;
    for (int i=0; i<n-1 ;i++){
        if (SegAcross(p,t,v[i],v[i+1])){ cnt++; continue; }
        if (v[i].x<v[i+1].x) cnt+=DotOnSeg(v[i],p,t);
        else                 cnt+=DotOnSeg(v[i+1],p,t);
    }
    return cnt&1;
}   /* 返回点是否严格在多边形内(不包含边界);采用垂直射线法 */

inline double PolyArea(point *v, int n){
    double area=0;
    for (int i=1; i<n-1; i++) area+=(v[i]-v[0])*(v[i+1]-v[0]);
    return fabs(area/2);
}   /* 返回多边形面积 */

int ConvexHull (point *p, int n, VP &v){
    sort(p,p+n);
    point q[n];
    int r=0;
    for (int i=0; i<n; i++){
        while (r>1 && SideLD(q[r-2],q[r-1],p[i])!=1) r--;
        q[r++]=p[i];
    }   /* 从左下向右扫 */
    int l=r;
    for (int i=n-2; i>=0; i--){
        while (r>l && SideLD(q[r-2],q[r-1],p[i])!=1) r--;
        q[r++]=p[i];
    }   /* 从右上向左扫 */
    if (n!=1) r--;  /* 两次扫都会让左下角的点入队,所以要删去一个 */
    for (int i=0; i<r; i++) v.push_back(q[i]);
    return r;
}   /* 求二维凸包,p为二维平面上的点,凸包顶点存在v内,逆时针方向;SideLD!=1则无三点共线,<0则保留共线点 */ /* tested */


/***************** Part three: Circle  *****************/


inline int LineCircleIntersection(point &c, double r, point &l1, point &l2, VP &p){
    point t=DotProLine(c,l1,l2);
    double r2=r*r, d2=sqr(DotToLine(c,l1,l2));
    if (sgn(r2-d2)<0) return 0;
    if (zero(r2-d2)){ p.push_back(t); return 1; }
    point v=(l1-t)*sqrt((r2-d2)/(l1-t).sqrlen());
    p.push_back(t+v);
    p.push_back(t-v);
    return 2;
}   /* 求圆与直线交点,返回值为交点个数,交点存在VP p内 */ /* tested */

inline int CirclesIntersection(point &c1, double r1, point &c2, double r2, VP &p){
    double rr=sqr(r1+r2), d2=(c2-c1).sqrlen(), d=sqrt(d2);
    if (zero(d2) && zero(r1-r2)) return -1;                             /* 重合 */
    if (sgn(fabs(r1-r2)-d)>0) return 0;                                 /* 内含 */
    if (sgn(rr-d2)<0) return 0;                                         /* 相离 */
    if (sgn(rr-d2)==0){ p.push_back(c1+(c2-c1).trunc(r1)); return 1; }  /* 相切 */
    double ang=acos((d2+r1*r1-r2*r2)/(2*d*r1));
    point p1=c1+((c2-c1)*r1).rotate(ang)/d;
    point p2=c1+((c2-c1)*r1).rotate(-ang)/d;
    p.push_back(p1);
    p.push_back(p2);
    return 2;
}   /* 求圆与圆的交点,返回值为交点个数,交点存在VP p内 */ /* tested */

double InscribedCircle(point &p1, point &p2, point &p3, point &c){
    double A=(p2-p3).length(), B=(p3-p1).length(), C=(p1-p2).length();
    c=(p1*A+p2*B+p3*C)/(A+B+C);
    return DotToSeg(c,p1,p2);
}   /* 求三角形内切圆,圆心为c,返回值为半径(需满足三点不共线) */ /* tested */

double CircumscribedCircle(point &p1, point &p2, point &p3, point &c){
    point B=p2-p1, C=p3-p1;
    double S=(B*C)*2.0;
    c.x=(C.y*(B.sqrlen())-B.y*(C.sqrlen()))/S+p1.x;
    c.y=(B.x*(C.sqrlen())-C.x*(B.sqrlen()))/S+p1.y;
    return (c-p1).length();
}   /* 求三角形外切圆,圆心为c,返回值为半径(需满足三点不共线) */ /* tested */

int TangentsP(point &p, point &c, double &r, VP &v){
    double d=(c-p).length();
    if (sgn(d-r)<0) return 0;
    if (sgn(d-r)==0){ v.push_back((c-p).rotate(PI/2)); return 1; }
    double ang=asin(r/d);
    v.push_back((c-p).rotate(ang));
    v.push_back((c-p).rotate(-ang));
    return 2;
}   /* 求所有过p点的圆c的切线,v中为这些切线的方向向量,返回值为切线数 */ /* tested */


/***************** Part four: Plane    *****************/


int HalfPlaneIntersection(line *L, int n, VP p){
    sort(L,L+n);
    point v[n];
    line q[n];
    q[0]=L[0];
    int l=0,r=0;
    for (int i=1; i<n; i++){
        while (l<r && SideLD(L[i].s,L[i].t,v[r-1])!=1) r--;
        while (l<r && SideLD(L[i].s,L[i].t,v[l])!=1) l++;
        q[++r]=L[i];
        if (Parallal(q[r-1].s,q[r-1].t,q[r].s,q[r].t)){
            r--;
            if (SideLD(q[r].s,q[r].t,L[i].s)==1) q[r]=L[i];
        }
        if (l<r) v[r-1]=Intersection(q[r-1].s,q[r-1].t,q[r].s,q[r].t);
    }
    while (l<r && SideLD(q[l].s,q[l].t,v[r-1])!=1) r--;
    if (r-l<=1) return 0;
    v[r]=Intersection(q[r].s,q[r].t,q[l].s,q[l].t);
    for (int i=l; i<=r; i++) p.push_back(v[i]);
    return p.size();
}   /* 求半平面交,p存围成相交区域的凸包上的点;若相交区域无界则需添加一个大矩形代替平面 */ /* tested */

}}}
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 1000;
const double eps = 1e-8, PI = atan2(0, -1);
const double INF=1e10;
inline double sqr(double x){ return x * x; }
inline bool zero(double x){
    return (x > 0 ? x : -x) < eps;
}
inline int sgn(double x){
    return (x > eps ? 1 : (x + eps < 0 ? -1 : 0));
}
struct point{
    double x, y;
    point(double x, double y):x(x), y(y) {}
    point() {}
    bool operator == (const point & a) const{
        return sgn(x - a.x) == 0 && sgn(y - a.y) == 0;
        }
    bool operator != (const point & a) const{
        return sgn(x - a.x) != 0 || sgn(y - a.y) != 0;
        }
    bool operator  < (const point & a) const{
        return sgn(x - a.x) < 0 || sgn(x - a.x) == 0 && sgn(y - a.y) < 0;
        }
    point operator + (const point & a) const{
        return point(x + a.x, y + a.y);
        }
    point operator - (const point & a) const{
        return point(x - a.x, y - a.y);
        }
    point operator * (const double & a) const{
        return point(x * a, y * a);
        }
    point operator / (const double & a) const{
        return point(x / a, y / a);
        }
    double operator * (const point & a) const{
        return x * a.y - y * a.x;
        }//xmult
    double operator ^ (const point & a) const{
        return x * a.x + y * a.y;
        }//dmult
    double sqrlen() const{ return sqr(x) + sqr(y); }
    double length() const{ return sqrt(sqrlen()); }
    point trunc(double a) const{
        return (*this) * (a / length());
        }
    point rotate(double ang) const{
        point p(sin(ang), cos(ang));
        return point((*this) * p, (*this) ^ p);
        }
    point rotate(const point & a) const{
        point p(-a.y, a.x); p = p.trunc(1.0);
        return point((*this) * p, (*this) ^ p);
        }
};
struct line{
    point s,t;
    double ang;
    line() {}
    line(point s,point t):s(s), t(t){ ang=atan2(t.y-s.y, t.x-s.x); }
    bool operator < (const line &l) const { return ang<l.ang; }
};
typedef vector <point> VP;
/***************** Part one: Dot&Line *****************/
inline bool DotOnSeg(point &p, point &l1, point &l2){
    return zero((l2-l1)*(p-l1)) && sgn((l1-p)^(l2-p))<0;
}  /* 判断点是否在线段上(不含端点);若包含线段端点,需将<0改成<=0*/ /* tested */
inline double DotToSeg(point &p, point &l1, point &l2){
    point L=l2-l1;
    if (sgn((p-l1)^L)<=0) return (p-l1).length();/* 1 */
    if (sgn((p-l2)^L)> 0) return (p-l2).length();/* 2 */
    return fabs(L*(p-l1)/L.length());
}   /* 返回点到线段距离;去掉2为点到射线距离;去掉1,2为点到直线距离 */ /* tested */
inline double DotToLine(point &p, point &l1, point &l2){
    point L=l2-l1; return fabs(L*(p-l1)/L.length());
}   /* 返回点到直线距离 */ /* tested */
inline point DotProLine(point &p, point &l1, point &l2){
    point L=l2-l1;
    return l1+L*((p-l1)^L)/L.sqrlen();
}   /* 返回点在直线上的投影点 */ /* tested */
inline int SideLD(point l1, point l2, point p){
    return sgn((l2-l1)*(p-l1));
}   /* 判断点在有向直线<l1l2>的哪测,1为左侧,0在线上,-1在右侧 */
inline bool Parallal(point &u1, point &u2, point &v1, point &v2){
    return zero((u2-u1)*(v2-v1));
}   /* 判断两直线,射线,线段是否平行 */ /* tested */
inline bool SegAcross(point &u1, point &u2, point &v1, point &v2){
    return sgn(((u2-u1)*(v1-u1))*((u2-u1)*(v2-u1)))<0
        && sgn(((v2-v1)*(u1-v1))*((v2-v1)*(u2-v1)))<0;
}   /* 判断两线段是否规范相交(不包括端点) */
inline point Intersection(point &u1, point &u2, point &v1, point &v2){
    double t=((u2-u1)*(v1-u1))/((v2-v1)*(u2-u1));
    return v1+(v2-v1)*t;
}   /* 使用前需保证两直线,射线或者线段相交,返回其交点 */ /* tested */
/***************** Part two: Polygon   *****************/
inline bool DotInsidePoly(point &p, point *v, int n){
    int cnt=0;
    point t=p; t.y=INF;
    for (int i=0; i<n-1 ;i++){
        if (SegAcross(p,t,v[i],v[i+1])){ cnt++; continue; }
        if (v[i].x<v[i+1].x) cnt+=DotOnSeg(v[i],p,t);
        else                 cnt+=DotOnSeg(v[i+1],p,t);
    }
    return cnt&1;
}   /* 返回点是否严格在多边形内(不包含边界);采用垂直射线法 */
inline double PolyArea(point *v, int n){
    double area=0;
    for (int i=1; i<n-1; i++) area+=(v[i]-v[0])*(v[i+1]-v[0]);
    return fabs(area/2);
}   /* 返回多边形面积 */
int ConvexHull (point *p, int n, VP &v){
    sort(p,p+n);
    point q[n];
    int r=0;
    for (int i=0; i<n; i++){
        while (r>1 && SideLD(q[r-2],q[r-1],p[i])!=1) r--;
        q[r++]=p[i];
    }   /* 从左下向右扫 */
    int l=r;
    for (int i=n-2; i>=0; i--){
        while (r>l && SideLD(q[r-2],q[r-1],p[i])!=1) r--;
        q[r++]=p[i];
    }   /* 从右上向左扫 */
    if (n!=1) r--;  /* 两次扫都会让左下角的点入队,所以要删去一个 */
    for (int i=0; i<r; i++) v.push_back(q[i]);
    return r;
}   /* 求二维凸包,p为二维平面上的点,凸包顶点存在v内,逆时针方向;SideLD!=1则无三点共线,<0则保留共线点 */ /* tested */
/***************** Part three: Circle  *****************/
inline int LineCircleIntersection(point &c, double r, point &l1, point &l2, VP &p){
    point t=DotProLine(c,l1,l2);
    double r2=r*r, d2=sqr(DotToLine(c,l1,l2));
    if (sgn(r2-d2)<0) return 0;
    if (zero(r2-d2)){ p.push_back(t); return 1; }
    point v=(l1-t)*sqrt((r2-d2)/(l1-t).sqrlen());
    p.push_back(t+v);
    p.push_back(t-v);
    return 2;
}   /* 求圆与直线交点,返回值为交点个数,交点存在VP p内 */ /* tested */
inline int CirclesIntersection(point &c1, double r1, point &c2, double r2, VP &p){
    double rr=sqr(r1+r2), d2=(c2-c1).sqrlen(), d=sqrt(d2);
    if (zero(d2) && zero(r1-r2)) return -1;                             /* 重合 */
    if (sgn(fabs(r1-r2)-d)>0) return 0;                                 /* 内含 */
    if (sgn(rr-d2)<0) return 0;                                         /* 相离 */
    if (sgn(rr-d2)==0){ p.push_back(c1+(c2-c1).trunc(r1)); return 1; }  /* 相切 */
    double ang=acos((d2+r1*r1-r2*r2)/(2*d*r1));
    point p1=c1+((c2-c1)*r1).rotate(ang)/d;
    point p2=c1+((c2-c1)*r1).rotate(-ang)/d;
    p.push_back(p1);
    p.push_back(p2);
    return 2;
}   /* 求圆与圆的交点,返回值为交点个数,交点存在VP p内 */ /* tested */
double InscribedCircle(point &p1, point &p2, point &p3, point &c){
    double A=(p2-p3).length(), B=(p3-p1).length(), C=(p1-p2).length();
    c=(p1*A+p2*B+p3*C)/(A+B+C);
    return DotToSeg(c,p1,p2);
}   /* 求三角形内切圆,圆心为c,返回值为半径(需满足三点不共线) */ /* tested */
double CircumscribedCircle(point &p1, point &p2, point &p3, point &c){
    point B=p2-p1, C=p3-p1;
    double S=(B*C)*2.0;
    c.x=(C.y*(B.sqrlen())-B.y*(C.sqrlen()))/S+p1.x;
    c.y=(B.x*(C.sqrlen())-C.x*(B.sqrlen()))/S+p1.y;
    return (c-p1).length();
}   /* 求三角形外切圆,圆心为c,返回值为半径(需满足三点不共线) */ /* tested */
int TangentsP(point &p, point &c, double &r, VP &v){
    double d=(c-p).length();
    if (sgn(d-r)<0) return 0;
    if (sgn(d-r)==0){ v.push_back((c-p).rotate(PI/2)); return 1; }
    double ang=asin(r/d);
    v.push_back((c-p).rotate(ang));
    v.push_back((c-p).rotate(-ang));
    return 2;
}   /* 求所有过p点的圆c的切线,v中为这些切线的方向向量,返回值为切线数 */ /* tested */
/***************** Part four: Plane    *****************/
int HalfPlaneIntersection(line *L, int n, VP p){
    sort(L,L+n);
    point v[n];
    line q[n];
    q[0]=L[0];
    int l=0,r=0;
    for (int i=1; i<n; i++){
        while (l<r && SideLD(L[i].s,L[i].t,v[r-1])!=1) r--;
        while (l<r && SideLD(L[i].s,L[i].t,v[l])!=1) l++;
        q[++r]=L[i];
        if (Parallal(q[r-1].s,q[r-1].t,q[r].s,q[r].t)){
            r--;
            if (SideLD(q[r].s,q[r].t,L[i].s)==1) q[r]=L[i];
        }
        if (l<r) v[r-1]=Intersection(q[r-1].s,q[r-1].t,q[r].s,q[r].t);
    }
    while (l<r && SideLD(q[l].s,q[l].t,v[r-1])!=1) r--;
    if (r-l<=1) return 0;
    v[r]=Intersection(q[r].s,q[r].t,q[l].s,q[l].t);
    for (int i=l; i<=r; i++) p.push_back(v[i]);
    return p.size();
}   /* 求半平面交,p存围成相交区域的凸包上的点;若相交区域无界则需添加一个大矩形代替平面 */ /* tested */