计算几何_直线求交点_线段判相交
附上一水题
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); }