计算几何_直线求交点_线段判相交

附上一水题

 

double eps = 1e-8;

struct Point {
    double x, y;
    Point () {}
    Point (double _x, double _y) : x(_x), y(_y) {}
    
    void Read() {
        scanf("%lf%lf", &x, &y);
    }
    
    Point operator + (const Point &a) const { return Point(x + a.x, y + a.y); }
    
    // 叉积=0是指两向量平行(重合)
    Point operator - (const Point &a) const { return Point(x - a.x, y - a.y); }
    
    double operator ^ (const Point &a) const { return x * a.y - y * a.x; }  // 叉积 
    double operator * (const Point &a) const { return x * a.x + y * a.y; }  // 点积 
    
    // 定义给map set 之类用的.. 不要用极角 可能会出错 
    bool operator <  (const Point &a) const {
        if (x != a.x) return x < a.x;
        return y < a.y;
    }
};

struct Triangle {
    Point pt1, pt2, pt3;
};

typedef Point Vector;

double GetLenght(Vector A) { return A * A; } // 获取长度 
double GetLenght(Point A, Point B) { return sqrt((A.x-B.x)*(A.x-B.x) + (A.y-B.y)*(A.y-B.y)); }

double GetAngle(Vector A, Vector B) { return acos( (A*B) / GetLenght(A) / GetLenght(B)); } // 获取夹角[-pi/2, pi/2]

double GetArea(Vector A, Vector B) { return A ^ B; } // 四边形面积, 有正负 

// AB --> AC  A到B: B-A  
double GetArea(Point A, Point B, Point C) { return GetArea(B-A, C-A); }  //  四边形面积, 有正负 

double GetCross(Point st, Point ed1, Point ed2) { return (ed1 - st) ^ (ed2 - st); }


/*点定位*/

// 点是否在线段上 
// 原理:  
// 充要条件: 三点共线  同时 jpt 点在 线段 之间 
// --------> 三点共线  同时 jpt 在st ed组成的矩阵里 
bool IsPointInSegment(const Point &jpt, const Point &st, const Point &ed)
{
    if (GetCross(jpt, st, ed) == 0.0 && 
        min(st.x, ed.x) <= jpt.x &&
        max(st.x, ed.x) >= jpt.x &&
        max(st.y, ed.y) <= jpt.y &&
        max(st.y, ed.y) >= jpt.y)
        return true;
    return false;
}

// 点是否在三角形内部
// 原理:  
// 点与三角形三个顶点构成的三个三角形的面积之和 与 三角形是否一样.
// 如果一样说明点在三角形内部 
bool IsPointInTriangle(const Point &jpt, const Triangle &t)
{
    double s,s1, s2, s3;
    s = fabs(GetArea(t.pt1 - t.pt2, t.pt3 - t.pt2));
    s1  = fabs(GetArea(t.pt1 - jpt, t.pt2 - jpt));
    s1  = fabs(GetArea(t.pt1 - jpt, t.pt3 - jpt));
    s1  = fabs(GetArea(t.pt2 - jpt, t.pt3 - jpt));
    return s == s1 + s2 + s3;
} 

// 点在多边形内外
// 一  扫描法
// 二  叉乘判别法
// 三  角度和判别法
// 四  特别的 点在矩形或园中. 


// 点是否在矩形
// 矩形点是 st 和 ed 
bool IsPointInRectangle(const Point &jpt, const Point &st, const Point &ed) 
{
    
    if (min(st.x, ed.x) <= jpt.x &&
        max(st.x, ed.x) >= jpt.x &&
        min(st.y, ed.y) <= jpt.y &&
        max(st.y, ed.y) >= jpt.y) 
            return true;
    return false;
}

/*
两线段相交分为"规范相交"和"非规范相交"。 
"规范相交"指的是两条线段恰有唯一一个不是端点的公共点;
而如果一条线段的一个端点在另一条线段上,或者两条线段部分重合,则视为“非规范相交”,
*/ 
bool IsSegmentIntersect(const Point &A, const Point &B, const Point &C, const Point &D) {
    
    //  快速排斥 好理解版本  判断在不在矩形内.  重叠情况也可以判断 
    if (!IsPointInRectangle(C, A, B) && !IsPointInRectangle(D, A, B)) return false;
    if (GetCross(A, C, B) * GetCross(A, D, B) <= 0.0 &&
        GetCross(C, A, D) * GetCross(C, B, D) <= 0.0)  
        return true;
    return false;
    
    // 前3句是快速排斥 
    // 后2句是跨立实验  <= 表示允许重叠 端点在另一线段上 < 则不允许 
/*    if (max(A.x, B.x) >= min(C.x, D.x) &&
        min(A.x, B.x) <= max(C.x, D.x) &&
        max(A.y, B.y) >= min(C.y, D.y) &&
        GetCross(A, C, B) * GetCross(A, D, B) <= 0.0 &&
        GetCross(C, A, D) * GetCross(C, B, D) <= 0.0 )
        return true;
    return false;*/
} 

struct Segment {
    Point st;
    Point ed;
    int id;
    void Read() {
        scanf("%lf%lf%lf%lf", &st.x, &st.y, &ed.x, &ed.y);
    } 
    Segment() {}
    Segment(Point A, Point B) { st = A; ed = B; } 
};

bool IsSegmentIntersect(const Segment &A,const Segment &B) {
    return IsSegmentIntersect(A.st, A.ed, B.st, B.ed);
}

// Ax + By + C = 0
// y = kX + b; 使用克拉默法则的时候需要注意C的符号. 
struct Line {
    Point st;
    Point ed;
    double A, B, C, k, b;
//    Vector v;

    void Read() {
        scanf("%lf%lf%lf%lf", &st.x, &st.y, &ed.x, &ed.y);
        init();
    //    printf("%lf %lf %lf \n", A, B, C);
    }
    Line () {}
    Line (Point A, Point B) {
        st = A;
        ed = B;
        init();
    }
    Line (Segment s_g) {
        st = s_g.st;
        ed = s_g.ed;
        init();
    }
    
    void init() {
        if (ed.x - st.x == 0.0) {
             k = 999999999999.99;
             b = st.y;
        } else {
            k = (ed.y - st.y) / (ed.x - st.x);
            b = st.y - k * st.x;
        }
        A = st.y - ed.y;
        B = ed.x - st.x;
        C = st.x * (ed.y - st.y) - st.y * (ed.x - st.x);
//        v = ed - st;
    }
};

// 判断两直线是否共线 
bool IsLineLineCollinear(const Line &a,const Line &b) {
    return fabs(((a.ed - a.st) ^ (b.ed - b.st))) < eps ? true : false;
}

// 判断两直线是否平行
bool IsLineLineParallel(const Line &a, const Line &b) {
    if (!IsLineLineCollinear(a, b)) return false;
    return fabs(((b.st - a.st) ^ (b.ed - a.st))) < eps ? false : true;
}

// 判断两直线是否重叠 
bool IsLineLineOver(const Line &a, const Line &b) {
    if (!IsLineLineCollinear(a, b)) return false;
    return fabs(((b.st - a.st) ^ (b.ed - a.st))) < eps ? true : false;
}


// 使用前需 确保直线已经调用 init()初始化,  是根据已知直线2点来求的.
// 使用克拉默法则 
Point GetLineLineIntersect(const Line &a, const Line &b) {
    Point pt = a.st;
    if (IsLineLineCollinear(a, b)) return pt;  // 如果共线 或者平行 瞎返回 
    
    double D = a.A * b.B - a.B * b.A;
    double D1 = (-a.C) * b.B - a.B * (-b.C);
    double D2 = a.A * (-b.C) - b.A * (-a.C);
    pt.x = D1 / D;
    pt.y = D2 / D;
    return pt;
}


// 跨立实验的原理
// 如果直线AB 跨立 CD 那么有 (AC ^ AB) * (AD ^ AB) > 0
// 所以跨立实验要判2个GetCross   
bool IsLineSegmentIntersect(const Line &line, const Segment &s_g) {
    if (GetCross(line.st, s_g.st, line.ed) * GetCross(line.st, s_g.ed, line.ed) > 0.0 ) return true;
    return false;
} 

//  叉积极角排序. 
//  以Qcmppt为起点 做极角排序. 
Point Qcmppt; 
bool cmp_j_j_j(const Point &A, const Point &B) {  
    int m = (A-Qcmppt) ^ (B-Qcmppt); 
    if (m != 0.0) return m > 0.0;
    if (A.x != B.x)  return A.x < B.x;
    return false;
}

// 求多边形面积, 从pts从1开始求面积  有n个点 
// 按照极角排序 
double GetArea(Point pts[], int ss[], int n)
{
    int i;
    double res = 0;
    // 只划分成n-2个三角形.  
    for (i=2; i<n; ++i) 
        res += GetCross(pts[ss[1]], pts[ss[i]], pts[ss[i+1]]); 
    res = res / 2.0; 
    return fabs(res);
}

// 求多边形周长  也需要先排序. 
double GetLenght(Point pts[], int ss[], int n)
{
    int i;
    double res = GetLenght(pts[ss[1]], pts[ss[n]]);
    // 只划分成n-2个三角形.  
    for (i=1; i<n; ++i) 
        res += GetLenght(pts[ss[i]], pts[ss[i+1]]);
    return fabs(res);
} 

 

posted @ 2018-08-03 14:20  过路人1998  阅读(354)  评论(0编辑  收藏  举报