【模板】 计几有关圆的模板

1 Vector Rotate(Vector A, double rad) { 2 return Vector(A.x * cos(rad) - A.y * sin(rad), A.x * sin(rad) + A.y * cos(rad)); 3 } 4 double angle(Vector v) {return atan2(v.y, v.x);} 5 struct Circle { 6 Point c; 7 double r; 8 Circle(Point c, double r) { 9 this->c = c; 10 this->r = r; 11 } 12 Point point(double a) { 13 return Point(c.x + cos(a) * r, c.y + sin(a) * r); 14 } 15 }; 16 17 Vector AngleBisector(Point p, Vector v1, Vector v2){//给定两个向量,求角平分线 18 double rad = Angle(v1, v2); 19 return Rotate(v1, dcmp(Cross(v1, v2)) * 0.5 * rad); 20 } 21 22 //求直线与圆的交点 23 int getLineCircleIntersection(Point p, Vector v, Circle c, vector<point> &sol) { 24 double a1 = v.x, b1 = p.x - c.c.x, c1 = v.y, d1 = p.y - c.c.y; 25 double e1 = a1 * a1 + c1 * c1, f1 = 2 * (a1 * b1 + c1 * d1), g1 = b1 * b1 + d1 * d1 - c.r * c.r; 26 double delta = f1 * f1 - 4 * e1 * g1, t; 27 if(dcmp(delta) < 0) return 0; 28 else if(dcmp(delta) == 0){ 29 t = (-f1) / (2 * e1); 30 sol.push_back(p + v * t); 31 return 1; 32 } else{ 33 t = (-f1 + sqrt(delta)) / (2 * e1); sol.push_back(p + v * t); 34 t = (-f1 - sqrt(delta)) / (2 * e1); sol.push_back(p + v * t); 35 return 2; 36 } 37 } 38 39 //点到圆的切线 40 int getTangents(Point p, Circle C, Vector *v) { 41 Vector u = C.c - p; 42 double dist = Length(u); 43 if (dist < C.r) return 0; 44 else if (dcmp(dist - C.r) == 0) { 45 v[0] = Rotate(u, PI / 2); 46 return 1; 47 } else { 48 double ang = asin(C.r / dist); 49 v[0] = Rotate(u, -ang); 50 v[1] = Rotate(u, +ang); 51 return 2; 52 } 53 } 54 55 //两圆公切线 56 //a[i], b[i]分别是第i条切线在圆A和圆B上的切点 57 int getCircleTangents(Circle A, Circle B, Point *a, Point *b) { 58 int cnt = 0; 59 if (A.r < B.r) { swap(A, B); swap(a, b); } 60 //圆心距的平方 61 double d2 = (A.c.x - B.c.x) * (A.c.x - B.c.x) + (A.c.y - B.c.y) * (A.c.y - B.c.y); 62 double rdiff = A.r - B.r; 63 double rsum = A.r + B.r; 64 double base = angle(B.c - A.c); 65 //重合有无限多条 66 if (d2 == 0 && dcmp(A.r - B.r) == 0) return -1; 67 //内切 68 if (dcmp(d2 - rdiff * rdiff) == 0) { 69 a[cnt] = A.point(base); 70 b[cnt] = B.point(base); 71 cnt++; 72 return 1; 73 } 74 //有外公切线 75 double ang = acos((A.r - B.r) / sqrt(d2)); 76 a[cnt] = A.point(base + ang); b[cnt] = B.point(base + ang); cnt++; 77 a[cnt] = A.point(base - ang); b[cnt] = B.point(base - ang); cnt++; 78 79 //一条内切线,两条内切线 80 if (dcmp(d2 - rsum*rsum) == 0) { 81 a[cnt] = A.point(base); b[cnt] = B.point(PI + base); cnt++; 82 } else if (dcmp(d2 - rsum*rsum) > 0) { 83 double ang = acos((A.r + B.r) / sqrt(d2)); 84 a[cnt] = A.point(base + ang); b[cnt] = B.point(pi+base + ang); cnt++; 85 a[cnt] = A.point(base - ang); b[cnt] = B.point(pi+base - ang); cnt++; 86 } 87 return cnt; 88 } 89 90 //求经过点p1,与直线(p2, w)相切,半径为r的一组圆 91 int CircleThroughAPointAndTangentToALineWithRadius(Point p1, Point p2, Vector w, double r, vector<point> &sol) { 92 Circle c1 = Circle(p1, r); 93 double t = r / Length(w); 94 Vector u = Vector(-w.y, w.x); 95 Point p4 = p2 + u * t; 96 int tot = getLineCircleIntersection(p4, w, c1, sol); 97 u = Vector(w.y, -w.x); 98 p4 = p2 + u * t; 99 tot += getLineCircleIntersection(p4, w, c1, sol); 100 return tot; 101 } 102 103 //给定两个向量,求两向量方向内夹着的圆的圆心。圆与两线均相切,圆的半径已给定 104 Point Centre_CircleTangentTwoNonParallelLineWithRadius(Point p1, Vector v1, Point p2, Vector v2, double r){ 105 Point p0 = GetLineIntersection(p1, v1, p2, v2); 106 Vector u = AngleBisector(p0, v1, v2); 107 double rad = 0.5 * Angle(v1, v2); 108 double l = r / sin(rad); 109 double t = l / Length(u); 110 return p0 + u * t; 111 } 112 113 //求与两条不平行的直线都相切的4个圆,圆的半径已给定 114 int CircleThroughAPointAndTangentALineWithRadius(Point p1, Vector v1, Point p2, Vector v2, double r, Point *sol) { 115 int ans = 0; 116 sol[ans++] = Centre_CircleTangentTwoNonParallelLineWithRadius(p1, v1, p2, v2, r); 117 sol[ans++] = Centre_CircleTangentTwoNonParallelLineWithRadius(p1, v1 * -1, p2, v2, r); 118 sol[ans++] = Centre_CircleTangentTwoNonParallelLineWithRadius(p1, v1, p2, v2 * -1, r); 119 sol[ans++] = Centre_CircleTangentTwoNonParallelLineWithRadius(p1, v1 * -1, p2, v2 * -1, r); 120 return ans; 121 } 122 123 //求与两个相离的圆均外切的一组圆,三种情况 124 int CircleTangentToTwoDisjointCirclesWithRadius(Circle c1, Circle c2, double r, Point *sol){ 125 double dis1 = c1.r + r + r + c2.r; 126 double dis2= Length(c1.c - c2.c); 127 if(dcmp(dis1 - dis2) < 0) return 0; 128 Vector u = c2.c - c1.c; 129 double t = (r + c1.r) / Length(u); 130 if(dcmp(dis1 - dis2)==0){ 131 Point p0 = c1.c + u * t; 132 sol[0] = p0; 133 return 1; 134 } 135 double aa = Length(c1.c - c2.c); 136 double bb = r + c1.r, cc = r + c2.r; 137 double rad = acos((aa * aa + bb * bb - cc * cc) / (2 * aa * bb)); 138 Vector w = Rotate(u, rad); 139 Point p0 = c1.c + w * t; 140 sol[0] = p0; 141 w = Rotate(u, -rad); 142 p0 = c1.c + w * t; 143 sol[1] = p0; 144 return 2; 145 }
常用的:

1 // 点到线段最短距离 2 double Point_to_Seg(Point p,Point a,Point b){ 3 if( sgn( dot(a,p,b) ) < 0 ) return dist(p,a); 4 if( sgn( dot(b,p,a) ) < 0 ) return dist(p,b); 5 return fabs( cross(p,a,b) ) / dist(a,b); 6 } 7 struct Circle{ 8 Point o; 9 vector<int> v; 10 //圆心发出的射线 11 Point getPoint(double deta){ 12 return (Point){o.x + cos(deta)*R , o.y + sin(deta)*R }; 13 } 14 //判断线段是否和圆相交 15 bool is_insert(Point a,Point b){ 16 double d; 17 if( dist(a,b) < eps ) d = dist(a,o); 18 else d = Point_to_Seg(o,a,b); 19 if( sgn(d - R) < 0 ) return 1; 20 return 0; 21 } 22 }cir[10]; 23 //点到圆切点 24 int getTangentPoints(Point p,Circle c,Point& r1,Point& r2){ 25 double d = dist(p,c.o); 26 if( sgn(d-R) < 0 ) return 0; 27 else if( sgn(d-R) == 0 ){ 28 r1 = p; 29 return 1; 30 } 31 else{ 32 double base = atan2(p.y - c.o.y,p.x - c.o.x); 33 double deta = acos( R/d ); 34 r1 = c.getPoint(base - deta); 35 r2 = c.getPoint(base + deta); 36 return 2; 37 } 38 } 39 //两圆公切线 40 //a[i], b[i]分别是第i条切线在圆A和圆B上的切点 41 int getCircleTangents(Circle A, Circle B, Point *a, Point *b) { 42 int cnt = 0; 43 if (A.r < B.r) { swap(A, B); swap(a, b); } 44 //圆心距的平方 45 double d2 = (A.c.x - B.c.x) * (A.c.x - B.c.x) + (A.c.y - B.c.y) * (A.c.y - B.c.y); 46 double rdiff = A.r - B.r; 47 double rsum = A.r + B.r; 48 double base = angle(B.c - A.c); 49 //重合有无限多条 50 if (d2 == 0 && dcmp(A.r - B.r) == 0) return -1; 51 //内切 52 if (dcmp(d2 - rdiff * rdiff) == 0) { 53 a[cnt] = A.point(base); 54 b[cnt] = B.point(base); 55 cnt++; 56 return 1; 57 } 58 //有外公切线 59 double ang = acos((A.r - B.r) / sqrt(d2)); 60 a[cnt] = A.point(base + ang); b[cnt] = B.point(base + ang); cnt++; 61 a[cnt] = A.point(base - ang); b[cnt] = B.point(base - ang); cnt++; 62 63 //一条内切线,两条内切线 64 if (dcmp(d2 - rsum*rsum) == 0) { 65 a[cnt] = A.point(base); b[cnt] = B.point(PI + base); cnt++; 66 } else if (dcmp(d2 - rsum*rsum) > 0) { 67 double ang = acos((A.r + B.r) / sqrt(d2)); 68 a[cnt] = A.point(base + ang); b[cnt] = B.point(pi+base + ang); cnt++; 69 a[cnt] = A.point(base - ang); b[cnt] = B.point(pi+base - ang); cnt++; 70 } 71 return cnt; 72 }
两圆相交模板:

1 // p1,p2是交点 2 inline bool operator<(const Point& lhs, const Point& rhs) { 3 if (fabs(lhs.x - rhs.x) > 1e-8) { 4 return lhs.x < rhs.x; 5 } else if (fabs(lhs.y - rhs.y) > 1e-8) { 6 return lhs.y < rhs.y; 7 } else { 8 return false; 9 } 10 } 11 double dis(Point a,Point b){ 12 return sqrt( (a.x - b.x)*(a.x - b.x) + (a.y - b.y)*(a.y - b.y)); 13 } 14 double sqr(double x){return x*x;} 15 bool intersection(Point o1, double r1, Point o2, double r2, Point& p1, Point& p2) { 16 double d = dis(o1, o2); 17 if (d < fabs(r1 - r2) - eps || d > r1 + r2 + eps) { 18 return false; 19 } 20 double cosa = (sqr(r1) + sqr(d) - sqr(r2)) / (2 * r1 * d); 21 double sina = sqrt(max(0.0, 1.0 - sqr(cosa))); 22 p1 = p2 = o1; 23 p1.x += r1 / d * ((o2.x - o1.x) * cosa + (o2.y - o1.y) * -sina); 24 p1.y += r1 / d * ((o2.x - o1.x) * sina + (o2.y - o1.y) * cosa); 25 p2.x += r1 / d * ((o2.x - o1.x) * cosa + (o2.y - o1.y) * sina); 26 p2.y += r1 / d * ((o2.x - o1.x) * -sina + (o2.y - o1.y) * cosa); 27 return true; 28 }
三点求圆心:

1 Point core(Point a,Point b,Point c){ 2 Point A=(Point){b.x-a.x,c.x-a.x}*2 , B = (Point){b.y-a.y,c.y-a.y}*2; 3 Point C = (Point){ len2(b)-len2(a),len2(c)-len2(a) }; 4 return (Point){ cross(B,C)/cross(B,A),cross(A,C)/cross(A,B) }; 5 }