二维几何基础
Posted blueheart0621
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二维几何基础相关的知识,希望对你有一定的参考价值。
1. 点、线、凸边形
1 /******************************************************* 2 二维几何基础 3 【注意】数组下标从1开始。 4 *******************************************************/ 5 6 #include <iostream> 7 #include <cstdio> 8 #include <cstdlib> 9 #include <cmath> 10 #include <iomanip> 11 #include <algorithm> 12 #include <string> 13 #define re register 14 #define il inline 15 #define ll long long 16 #define ld long double 17 using namespace std; 18 const ll MAXN = 1e5+5; 19 const ll INF = 1e9; 20 const ld EPS = 1e-9; 21 22 //点坐标 23 struct POINT 24 { 25 ld x, y; 26 POINT() : x(0), y(0) {} 27 POINT(ld _x, ld _y) : x(_x), y(_y) {} 28 }; 29 //向量 30 typedef POINT VECTOR; 31 32 POINT xy[MAXN]; //顺时针多边形顶点存储 33 POINT xyB[MAXN]; //判断点存储 34 35 36 //符号函数 37 ll sgn(ld x) {return x < -EPS ? -1 : x > EPS;} 38 //向量+向量=向量,点+向量=点 39 VECTOR operator + (VECTOR a, VECTOR b) {return {a.x+b.x,a.y+b.y};} 40 //向量-向量=向量,点-点=向量 41 VECTOR operator - (VECTOR a, VECTOR b) {return {a.x-b.x,a.y-b.y};} 42 //向量*数=向量 43 VECTOR operator * (VECTOR a, ld k) {return {a.x*k,a.y*k};} 44 VECTOR operator * (ld k, VECTOR a) {return {a.x*k,a.y*k};} 45 //向量/数=向量 46 VECTOR operator / (VECTOR a, ld k) {return {a.x/k,a.y/k};} 47 //向量==向量,点==点 48 bool operator == (const VECTOR a, const VECTOR b) {return !sgn(a.x-b.x) && !sgn(a.y-b.y);} 49 //向量偏序关系(先x轴再y轴) 50 bool operator < (const VECTOR a, const VECTOR b) {return !sgn(a.x-b.x) ? a.y < b.y : a.x < b.x;} 51 //向量旋转(逆时针) 52 VECTOR rot(VECTOR a, ld sita) {return {a.x*cos(sita)-a.y*sin(sita),a.x*sin(sita)+a.y*cos(sita)};} 53 //取下端点 54 POINT min(POINT p1, POINT p2) {return p1.y<p2.y ? p1:p2;} 55 //取上端点 56 POINT max(POINT p1, POINT p2) {return p1.y<p2.y ? p2:p1;} 57 //点积 58 ld dot(POINT p1, POINT p2, POINT p) {return (p.x-p1.x)*(p2.x-p1.x)+(p.y-p1.y)*(p2.y-p1.y);} //(三点式:共p1端点) 59 ld dot(VECTOR a, VECTOR b) {return a.x*b.x+a.y*b.y;} //向量式 60 //叉积 61 ld cross(POINT p1, POINT p2, POINT p) {return (p.x-p1.x)*(p2.y-p1.y)-(p.y-p1.y)*(p2.x-p1.x);} //(三点式:共p1端点) 62 ld cross(VECTOR a, VECTOR b) {return a.x*b.y-a.y*b.x;} //向量式(aXb) 63 //向量长度 64 ld vlen(VECTOR a) {return sqrt(dot(a,a));} 65 //向量夹角余弦值 66 ld vcos(VECTOR a, VECTOR b) {return dot(a,b)/(vlen(a)*vlen(b));} 67 //向量夹角正弦值 68 ld vsin(VECTOR a, VECTOR b) {return fabs(cross(a,b))/(vlen(a)*vlen(b));} 69 //向量极角 70 ld vagl(VECTOR a, VECTOR b) {return acos(dot(a,b)/(vlen(a)*vlen(b)));} //向量间 71 ld vagl(VECTOR a) {return acos(a.x/vlen(a));} 72 //求直线斜率【注意】确保斜率存在 73 ld slope(VECTOR a) {return a.y/a.x;} //向量式 74 ld slope(POINT p, POINT q) {return (p.y-q.y)/(p.x-q.x);} //两点式 75 //单位向量【注意】确保非零向量 76 VECTOR unit(VECTOR a) {return a/vlen(a);} 77 //两直线交点 78 POINT intersectline(POINT p, VECTOR v, POINT q, VECTOR w) {return p+v*cross(w,p-q)/cross(v,w);} //(参数式:P=P0+t*v,P0为直线上某一点,v为直线的方向向量) 79 //点在直线上的投影 80 POINT proline(POINT a, POINT b, POINT p) {return a+(b-a)*(dot(b-a,p-a)/dot(b-a,b-a));} 81 //点关于直线的对称点 82 POINT refline(POINT a, POINT b, POINT p) {return proline(a,b,p)*2-p;} 83 //判断两直线是否平行 84 bool parallel(POINT p1, POINT p2, POINT q1, POINT q2) {return !sgn(cross(p2-p1,q2-q1)) && sgn(cross(p1-q1,p2-q1));} 85 //判断两直线重合 86 bool superposition(POINT p1, POINT p2, POINT q1, POINT q2) {return !sgn(cross(p2-p1,q2-q1)) && !sgn(cross(p1-q1,p2-q1));} 87 //点到直线距离 88 ld disline(POINT a, POINT b, POINT p) {return fabs(cross(b-a,p-a))/vlen(b-a);} //不取绝对值得到的是有向距离 89 //点到线段距离(两种情况:点的投影在线段上,则为垂直距离;点的投影不在线段上,则为到两端距离的最小值) 90 ld disseg(POINT a, POINT b, POINT p) {return a==b ? vlen(p-a):dot(b-a,p-a)<0 ? vlen(p-a):dot(b-a,p-b)>0 ? vlen(p-b):fabs(cross(b-a,p-a))/vlen(b-a);} 91 //线段相交判断(严格) 92 bool intersectseg(POINT p1, POINT p2, POINT q1, POINT q2) {return cross(p1-q1,q2-q1)*cross(p2-q1,q2-q1)<0 && cross(q1-p1,p2-p1)*cross(q2-p1,p2-p1)<0;} 93 //判断点p(x,y)是否在线段p1p2上(两种情况:1.包括端点;2.不包含端点) 94 bool onseg(POINT p1, POINT p2, POINT p) {return !sgn(cross(p1,p2,p)) && sgn(dot(p,p1,p2))<=0;} //包含端点 95 bool onseg_strict(POINT p1, POINT p2, POINT p) {return !sgn(cross(p1,p2,p)) && sgn(dot(p,p1,p2))<0;} //不包含端点 96 97 //点射法判断点是否在多边形内部(边界也算在内部) 98 //复杂度O(n)(不论凹凸) 99 bool inpolygon_dot(ld x, ld y, ll n) 100 { 101 ll cnt = 0; 102 for(re ll i = 0; i < n; ++i) 103 { 104 POINT p1 = min(xy[i+1], xy[(i+1)%n+1]); //取下端点 105 POINT p2 = max(xy[i+1], xy[(i+1)%n+1]); //取上端点 106 //特判点在线段上 107 if(onseg(p1,p2,{x,y})) return true; 108 //从点(x,y)向x反方向引出射线 109 //计算点射到的多边形的边数边数 110 if(sgn(p1.y-y)<0 && sgn(y-p2.y) <=0 && sgn(cross(p1,p2,{x,y}))>0) ++cnt; 111 } 112 if(cnt%2) return true; 113 else return false; 114 } 115 116 //二分法判断点是否在多边形内部(不包括边界) 117 //复杂度O(logn)(要求凸多边形) 118 bool inpolygon_bis(ld x, ld y, ll n) 119 { 120 POINT p = {x,y}; 121 ll l = 2, r = n; 122 //判断是否在幅角最小和最大的两条边上 123 if(onseg(xy[1],xy[l],p) || onseg(xy[1],xy[n],p)) return false; 124 //二分法判断是否在多边形内部 125 while(l < r) 126 { 127 ll mid = (l+r)>>1; //【注意】如此取中点最终:r==l或r==l-1(只有此两种情况) 128 ll d = sgn(cross(xy[mid]-xy[1],p-xy[1])); 129 if(d < 0) l = mid+1; 130 else if(d > 0) r = mid-1; 131 else 132 { 133 if(onseg_strict(xy[1],xy[mid],p)) 134 { 135 return true; 136 } 137 else 138 { 139 return false; 140 } 141 } 142 } 143 //判断在最终二分得到的对角线两端的边是否都满足严格在内的条件 144 if(l >= r && (sgn(cross(xy[l]-xy[l-1],p-xy[l-1]))>=0 || sgn(cross(xy[l%n+1]-xy[l],p-xy[l]))>=0)) 145 { 146 return false; 147 } 148 return true; 149 } 150 151 //计算多边形面积(凹凸均可) 152 ld polygonarea(ll n) 153 { 154 ld aera = 0; 155 for(re ll i = 0; i < n; ++i) 156 { 157 aera += cross(xy[i+1], xy[(i+1)%n+1]); 158 } 159 //计算出来的aera为有向面积 160 return fabs(aera)/2; 161 } 162 163 int main() 164 { 165 std::ios::sync_with_stdio(false); 166 //判断直线平行/重合/相交 167 ll n; 168 cin >> n; 169 cout << "INTERSECTING LINES OUTPUT" << endl; 170 for(re ll i = 1; i <= n; ++i) 171 { 172 POINT p1, p2, p3, p4; 173 cin >> p1.x >> p1.y >> p2.x >> p2.y; 174 cin >> p3.x >> p3.y >> p4.x >> p4.y; 175 if(parallel(p1,p2,p3,p4)) cout << "NONE" << endl; 176 else if(superposition(p1,p2,p3,p4)) cout << "LINE" << endl; 177 else 178 { 179 POINT q = intersectline(p1,p2-p1,p3,p4-p3); 180 cout << "POINT " << fixed << setprecision(2) << q.x << " " << q.y << endl; 181 } 182 } 183 cout << "END OF OUTPUT" << endl; 184 return 0; 185 /* 186 //计算多边形的面积 187 ll n; 188 while(true) 189 { 190 std::cin >> n; 191 if(!n) break; 192 for(re ll i = 1; i <= n; ++i) 193 { 194 std::cin >> xy[i].x; 195 std::cin >> xy[i].y; 196 } 197 std::cout << std::fixed << std::setprecision(1) << polygonarea(n) << std::endl; 198 } 199 return 0; 200 */ 201 /* 202 //判断点是否在多边形内(不包括边界) 203 ll n; 204 std::cin >> n; 205 for(re ll i = 1; i <= n; ++i) 206 { 207 std::cin >> xy[i].x; 208 std::cin >> xy[i].y; 209 } 210 ll m; 211 cin >> m; 212 for(re ll i = 1; i <= m; ++i) 213 { 214 cin >> xyB[i].x; 215 cin >> xyB[i].y; 216 } 217 for(re ll i = 1; i <= m; ++i) 218 { 219 if(!inpolygon_bis(xyB[i].x, xyB[i].y, n)) 220 { 221 printf("NO "); 222 return 0; 223 } 224 } 225 printf("YES "); 226 return 0; 227 */ 228 /* 229 //判断点是否在多边形内(包括边界) 230 ll N, M, k = 0; 231 while(!(std::cin >> N).eof()) 232 { 233 if(!N) break; 234 std::cin >> M; 235 if(k) printf(" "); 236 ++k; 237 for(re ll i = 1; i <= N; ++i) 238 { 239 std::cin >> xy[i].x; 240 std::cin >> xy[i].y; 241 //printf("(%f,%f) ", xy[i].x, xy[i].y); 242 } 243 printf("Problem %lld: ", k); 244 for(re ll i = 1; i <= M; ++i) 245 { 246 ld x, y; 247 std::cin >> x; 248 std::cin >> y; 249 //printf("(%f,%f) ", x, y); 250 if(inpolygon(x, y, N)) 251 { 252 printf("Within "); 253 } 254 else 255 { 256 printf("Outside "); 257 } 258 } 259 } 260 return 0; 261 */ 262 }
2. 圆
1 /***************************************************************** 2 圆 3 【注意】三角与反三角最多用一次(损失精度大)。 4 *****************************************************************/ 5 6 #include <bits/stdc++.h> 7 #include <iomanip> 8 #include <stack> 9 #include <cstdio> 10 #include <cmath> 11 #include <algorithm> 12 #define re register 13 #define il inline 14 #define ll long long 15 #define ld long double 16 using namespace std; 17 const ld PI = 3.14159265358979323846; 18 const ll MAXN = 5e5+5; 19 const ld INF = 1e9; 20 const ld EPS = 1e-10; 21 22 //点坐标 23 struct POINT 24 { 25 ld x, y; 26 POINT() : x(0), y(0) {} 27 POINT(ld _x, ld _y) : x(_x), y(_y) {} 28 }; 29 //向量 30 typedef POINT VECTOR; 31 32 //符号函数 33 ll sgn(ld x) {return x < -EPS ? -1 : x > EPS;} 34 //向量+向量=向量,点+向量=点 35 VECTOR operator + (VECTOR a, VECTOR b) {return {a.x+b.x,a.y+b.y};} 36 //向量-向量=向量,点-点=向量 37 VECTOR operator - (VECTOR a, VECTOR b) {return {a.x-b.x,a.y-b.y};} 38 //向量*数=向量 39 VECTOR operator * (VECTOR a, ld k) {return {a.x*k,a.y*k};} 40 VECTOR operator * (ld k, VECTOR a) {return {a.x*k,a.y*k};} 41 //向量/数=向量 42 VECTOR operator / (VECTOR a, ld k) {return {a.x/k,a.y/k};} 43 //向量==向量,点==点 44 bool operator == (const VECTOR a, const VECTOR b) {return !sgn(a.x-b.x) && !sgn(a.y-b.y);} 45 //向量偏序关系(先x轴再y轴) 46 bool operator < (const VECTOR a, const VECTOR b) {return !sgn(a.x-b.x) ? a.y < b.y : a.x < b.x;} 47 //取下端点 48 POINT min(POINT p1, POINT p2) {return p1.y<p2.y ? p1:p2;} 49 //取上端点 50 POINT max(POINT p1, POINT p2) {return p1.y<p2.y ? p2:p1;} 51 //点积 52 ld dot(POINT p1, POINT p2, POINT p) {return (p.x-p1.x)*(p2.x-p1.x)+(p.y-p1.y)*(p2.y-p1.y);} //(三点式:共p1端点) 53 ld dot(VECTOR a, VECTOR b) {return a.x*b.x+a.y*b.y;} //向量式 54 //叉积 55 ld cross(POINT p1, POINT p2, POINT p) {return (p.x-p1.x)*(p2.y-p1.y)-(p.y-p1.y)*(p2.x-p1.x);} //(三点式:共p1端点) 56 ld cross(VECTOR a, VECTOR b) {return a.x*b.y-a.y*b.x;} //向量式(aXb) 57 //向量长度 58 ld vlen(VECTOR a) {return sqrt(dot(a,a));} 59 //向量旋转(逆时针) 60 VECTOR rot(VECTOR a, ld sita) {return {a.x*cos(sita)-a.y*sin(sita),a.x*sin(sita)+a.y*cos(sita)};} 61 //取单位向量 62 VECTOR unit(VECTOR a) {return a/vlen(a);} 63 //取正交单位向量 64 VECTOR norm(VECTOR a) {return VECTOR(-a.y,a.x)/vlen(a);} 65 //向量夹角余弦值 66 ld vcos(VECTOR a, VECTOR b) {return dot(a,b)/(vlen(a)*vlen(b));} 67 //向量夹角正弦值 68 ld vsin(VECTOR a, VECTOR b) {return fabs(cross(a,b))/(vlen(a)*vlen(b));} 69 //向量极角 70 //【注意】使用系统反三角函数返回nan是因为超出了定义域(大多数由于精度造成) 71 //【注意】故使用系统反三角函数需处理边界 72 ld vagl(VECTOR a, VECTOR b) 73 { 74 ld d = dot(a,b)/(vlen(a)*vlen(b)); 75 if(sgn(d-1) == 0) return 0; 76 else if(sgn(d+1) == 0) return PI; 77 else return acos(d); 78 } //向量间[0,PI] 79 ld vagl(VECTOR a) {return atan2(a.y, a.x);} //单向量[-PI,PI] 80 81 //直线 82 struct LINE 83 { 84 POINT p; //直线上定点 85 VECTOR v; //直线方向向量 86 LINE() {} 87 //点向式 88 LINE(POINT _p, VECTOR _v):p(_p), v(_v) {} 89 //点角式 90 LINE(POINT _p, ld a):p(_p), v(VECTOR(cos(a), sin(a))) {} 91 //通过参数t唯一确定直线上点坐标(点向式:P = p + t*v) 92 POINT point(ld t) {return p + t*v;} 93 //平移向量r 94 LINE trans(VECTOR r) {return LINE(p+r,v);} 95 }; 96 97 //点到直线距离 98 ld PLdist(POINT p, LINE L) {return fabs(sin(vagl(p-L.p,L.v))*vlen(p-L.p));} 99 100 //圆 101 struct CIRCLE 102 { 103 POINT c; //圆心坐标 104 ld r; //半径 105 CIRCLE() {} 106 CIRCLE(POINT _c, ld _r):c(_c),r(_r) {} 107 //通过圆心角唯一确定圆上点坐标 108 POINT point(ld a) //a为圆心角 109 { 110 return POINT(c.x+cos(a)*r, c.y+sin(a)*r); 111 } 112 }; 113 114 //两点中垂线 115 //返回中垂线的个数 116 ll PerpendicularBisector(POINT p1, POINT p2, LINE &L) 117 { 118 if(p1 == p2) return 0; 119 L.p = (p1+p2)/2; 120 L.v = rot(p2-p1, PI/2); 121 return 1; 122 } 123 124 //两直线交点 125 POINT LLitesct; 126 127 //直线与直线的交点 128 //返回交点个数 129 ll LineLineIntersection(LINE L1, LINE L2, POINT &p) 130 { 131 if(sgn(cross(L1.v, L2.v)) == 0) 132 { 133 if(sgn(cross(L2.p-L1.p,L2.v) == 0)) return -1; //两直线重合 134 else return 0; //两直线平行 135 } 136 //以L1为准 137 ld t1 = -cross(L2.p-L1.p, L2.v)/cross(L2.v, L1.v); 138 p = L1.p + t1*L1.v; 139 //以L2为准 140 //ld t2 = -cross(L1.p-L2.p, L1.v)/cross(L1.v, L2.v); 141 //p = L2.p + t2*L2.v; 142 return 1; 143 } 144 145 //直线与圆交点数组 146 vector <POINT> LCitesct; 147 148 //直线与圆的交点 149 //解方程:(at+b)^2+(ct+d)^2 = r^2; 150 //化简得:et^2+ft+g = 0 151 //返回交点个数 152 ll LineCircleIntersection(LINE L, CIRCLE C, ld &t1, ld &t2, vector <POINT>& sol) 153 { 154 ld a = L.v.x, b = L.p.x-C.c.x, c = L.v.y, d = L.p.y-C.c.y; 155 ld e = a*a + c*c, f = 2*(a*b + c*d), g = b*b + d*d - C.r*C.r; 156 ld delta = f*f - 4*e*g; 157 if(sgn(delta) < 0) //相离 158 return 0; 159 else if(sgn(delta) == 0) //相切 160 { 161 t1 = t2 = -f/(2*e); 162 sol.push_back(L.point(t1)); 163 return 1; 164 } 165 else 166 { 167 t1 = (-f - sqrt(delta))/(2*e); 168 t2 = (-f + sqrt(delta))/(2*e); 169 sol.push_back(L.point(t1)); 170 sol.push_back(L.point(t2)); 171 return 2; 172 } 173 } 174 ll LineCircleIntersection(LINE L, CIRCLE C, POINT *p) 175 { 176 ld t1, t2; 177 ld a = L.v.x, b = L.p.x-C.c.x, c = L.v.y, d = L.p.y-C.c.y; 178 ld e = a*a + c*c, f = 2*(a*b + c*d), g = b*b + d*d - C.r*C.r; 179 ld delta = f*f - 4*e*g; 180 if(sgn(delta) < 0) //相离 181 return 0; 182 else if(sgn(delta) == 0) //相切 183 { 184 t1 = t2 = -f/(2*e); 185 p[0] = L.point(t1); 186 return 1; 187 } 188 else 189 { 190 t1 = (-f - sqrt(delta))/(2*e); 191 t2 = (-f + sqrt(delta))/(2*e); 192 p[0] = L.point(t1); 193 p[1] = L.point(t2); 194 return 2; 195 } 196 } 197 198 //圆与圆的交点数组 199 vector <POINT> CCitesct; 200 201 //两圆交点 202 ll CircleCircleIntersection(CIRCLE C1, CIRCLE C2, vector <POINT>& sol) 203 { 204 ld d = vlen(C1.c-C2.c); 205 if(sgn(d) == 0) 206 { 207 if(sgn(C1.r-C2.r) == 0) return -1; //两圆重合 208 else return 0; //同心相含 209 } 210 if(sgn(C1.r+C2.r-d) < 0) return 0; //相离 211 if(sgn(fabs(C1.r-C2.r)-d) > 0) return 0; //异心相含 212 213 //设两圆交点为p1,p2 214 ld a = vagl(C2.c - C1.c); //向量C1C2的极角 215 ld b = acos((C1.r*C1.r + d*d - C2.r*C2.r)/(2*C1.r*d)); //向量C1C2到C1P1(或C1P2)的夹角 216 POINT p1 = C1.point(a-b); 217 POINT p2 = C1.point(a+b); 218 sol.push_back(p1); 219 if(p1 == p2) return 1; //相切 220 sol.push_back(p2); //相交 221 return 2; 222 } 223 224 //圆切线方向向量 225 VECTOR LCtgt[2]; 226 227 //过定点作圆的切线 228 ll Tangents(POINT p, CIRCLE C, VECTOR *v) 229 { 230 VECTOR u = C.c - p; 231 ld dist = vlen(u); 232 if(dist < C.r) return 0; //点在圆内,没有切线 233 else if(sgn(dist-C.r) == 0) //点在圆上,一条切线 234 { 235 v[0] = rot(u, PI/2); 236 return 1; 237 } 238 else //点在圆外,两条切线 239 { 240 ld a = asin(C.r/dist); 241 v[0] = rot(u, -a); 242 v[1] = rot(u, +a); 243 return 2; 244 } 245 } 246 247 //两圆公切点(分别在两圆上) 248 POINT CCtgt1[4], CCtgt2[4]; 249 250 //两圆公切线 251 ll Tangents(CIRCLE C1, CIRCLE C2, POINT *a, POINT *b) 252 { 253 ll cnt = 0; 254 if(C1.r < C2.r) {swap(C1, C2); swap(a, b);} 255 ld d = dot(C1.c-C2.c, C1.c-C2.c); 256 ld rdiff = C1.r - C2.r; 257 ld rsum = C1.r + C2.r; 258 if(d < rdiff*rsum) return 0; //相含 259 ld base = atan2(C2.c.y-C1.c.y, C2.c.x-C1.c.x); //向量C1C2极角[-PI,PI] 260 if(sgn(d) == 0 && sgn(C1.r-C2.r) == 0) return -1; //重合 261 if(sgn(d-rdiff*rdiff) == 0) //内切 262 { 263 a[cnt] = C1.point(base); 264 b[cnt++] = C2.point(base); 265 return 1; 266 } 267 ld ang = acos((C1.r-C2.r)/sqrt(d)); //夹角[0,PI] 268 a[cnt] = C1.point(base+ang); 269 b[cnt++] = C2.point(base+ang); 270 a[cnt] = C1.point(base-ang); 271 b[cnt++] = C2.point(base-ang); 272 if(sgn(d-rsum*rsum)) //外切 273 { 274 a[cnt] = C1.point(base); 275 b[cnt++] = C2.point(base+PI); 276 } 277 else if(d > rsum*rsum) //相离 278 { 279 ld ang = acos((C1.r+C2.r)/sqrt(d)); 280 a[cnt] = C1.point(base+ang); 281 b[cnt++] = C2.point(base+ang); 282 a[cnt] = C1.point(base-ang); 283 b[cnt++] = C2.point(base-ang); 284 } 285 return cnt; //切点个数 286 } 287 288 //三点外接圆 289 //返回外接圆的个数 290 ll CircumscribedCircle(POINT a, POINT b, POINT c, CIRCLE &C) 291 { 292 if(sgn(cross(a-b, b-c)) == 0) return 0; //三点共线 293 LINE L1, L2; 294 PerpendicularBisector(a, b, L1); 295 PerpendicularBisector(b, c, L2); 296 POINT p; 297 LineLineIntersection(L1, L2, p); 298 C.c = p; 299 C.r = vlen(p-a); 300 return 1; 301 } 302 303 //三点内接圆 304 //返回内接圆的个数 305 ll InscribedCircle(POINT a, POINT b, POINT c, CIRCLE &C) 306 { 307 if(sgn(cross(a-b, b-c)) == 0) return 0; //三点共线 308 LINE L1 = LINE(a, (vagl(b-a)+vagl(c-a))/2), L2 = LINE(b, (vagl(a-b)+vagl(c-b))/2); 309 POINT p; 310 LineLineIntersection(L1, L2, p); 311 C.c = p; 312 C.r = PLdist(p,LINE(a,b-a)); 313 return 1; 314 } 315 316 317 int main() 318 { 319 //UVA-12304 320 //ios::sync_with_stdio(false); 321 string str; 322 while(cin >> str) 323 { 324 if(str == "CircumscribedCircle") 325 { 326 POINT a, b, c; 327 cin >> a.x >> a.y >> b.x >> b.y >> c.x >> c.y; 328 CIRCLE C; 329 CircumscribedCircle(a,b,c,C); 330 cout << fixed << "(" << C.c.x << "," << C.c.y << "," << C.r << ")" << endl; 331 } 332 else if(str == "InscribedCircle") 333 { 334 POINT a, b, c; 335 cin >> a.x >> a.y >> b.x >> b.y >> c.x >> c.y; 336 CIRCLE C; 337 InscribedCircle(a,b,c,C); 338 cout << fixed << "(" << C.c.x << "," << C.c.y << "," << C.r << ")" << endl; 339 } 340 //求已知圆的切线的极角 341 else if(str == "TangentLineThroughPoint") 342 { 343 CIRCLE C; 344 POINT p; 345 cin >> C.c.x >> C.c.y >> C.r >> p.x >> p.y; 346 VECTOR v[2]; 347 ll cnt = Tangents(p,C,v); 348 cout << "["; 349 if(cnt == 1) 350 { 351 ld r = vagl(v[0]); 352 ld a = (sgn(r) < 0 ? PI+r : sgn(r-PI) == 0 ? 0 : r)*180/PI; 353 cout << fixed << a; 354 } 355 else if(cnt == 2) 356 { 357 ld mxr = vagl(v[0]); 358 ld mir = vagl(v[1]); 359 ld mxa = (sgn(mxr) < 0 ? PI+mxr : sgn(mxr-PI) == 0 ? 0 : mxr)*180/PI; 360 ld mia = (sgn(mir) < 0 ? PI+mir : sgn(mir-PI) == 0 ? 0 : mir)*180/PI; 361 if(mxa < mia) swap(mxa,mia); 362 cout << fixed << mia << "," << mxa; 363 } 364 cout << "]" << endl; 365 } 366 //求过定点且与已知直线相切的圆的圆心 367 else if(str == "CircleThroughAPointAndTangentToALineWithRadius") 368 { 369 CIRCLE P; 370 POINT p1, p2; 371 cin >> P.c.x >> P.c.y >> p1.x >> p1.y >> p2.x >> p2.y >> P.r; 372 VECTOR v = rot(p2-p1,PI/2); 373 ld t = P.r/vlen(v); 374 LINE L1 = LINE(p1+t*v,p2-p1), L2 = LINE(p1-t*v,p2-p1); 375 ll cnt = 0; 376 ld t1, t2; 377 vector <POINT> C; 378 cnt += LineCircleIntersection(L1,P,t1,t2,C); 379 cnt += LineCircleIntersection(L2,P,t1,t2,C); 380 sort(C.begin(),C.end()); 381 cout << "["; 382 if(cnt == 1) cout << fixed << "(" << C[0].x << "," << C[0].y << ")"; 383 else if(cnt == 2) cout << fixed << "(" << C[0].x << "," << C[0].y << ")" << "," << "(" << C[1].x << "," << C[1].y << ")"; 384 cout << "]" << endl; 385 } 386 //求给定半径且与两条不相交直线相切的圆的圆心 387 else if(str == "CircleTangentToTwoLinesWithRadius") 388 { 389 POINT p1, p2, p3, p4; 390 ld r; 391 cin >> p1.x >> p1.y >> p2.x >> p2.y >> p3.x >> p3.y >> p4.x >> p4.y >> r; 392 LINE L1 = LINE(p1,p2-p1), L2 = LINE(p3,p4-p3); 393 VECTOR v1 = norm(p2-p1), v2 = norm(p4-p3); 394 LINE L11 = L1.trans(r*v1), L12 = L1.trans(-r*v1); 395 LINE L21 = L2.trans(r*v2), L22 = L2.trans(-r*v2); 396 vector <POINT> C; 397 POINT p; 398 LineLineIntersection(L11,L21,p); 399 C.push_back(p); 400 LineLineIntersection(L11,L22,p); 401 C.push_back(p); 402 LineLineIntersection(L12,L21,p); 403 C.push_back(p); 404 LineLineIntersection(L12,L22,p); 405 C.push_back(p); 406 sort(C.begin(),C.end()); 407 cout << "["; 408 cout << fixed << "(" << C[0].x << "," << C[0].y << ")" << "," << "(" << C[1].x << "," << C[1].y << ")" << ","; 409 cout << fixed << "(" << C[2].x << "," << C[2].y << ")" << "," << "(" << C[3].x << "," << C[3].y << ")"; 410 cout << "]" << endl; 411 412 } 413 else if(str == "CircleTangentToTwoDisjointCirclesWithRadius") 414 { 415 CIRCLE C1, C2; 416 ld r; 417 cin >> C1.c.x >> C1.c.y >> C1.r >> C2.c.x >> C2.c.y >> C2.r >> r; 418 C1.r += r, C2.r += r; 419 vector <POINT> p; 420 ll cnt = CircleCircleIntersection(C1,C2,p); 421 sort(p.begin(),p.end()); 422 cout << "["; 423 if(cnt == 1) 424 { 425 cout << fixed << "(" << p[0].x << "," << p[0].y << ")"; 426 } 427 else if(cnt == 2) 428 { 429 cout << fixed << "(" << p[0].x << "," << p[0].y << ")" << "," << "(" << p[1].x << "," << p[1].y << ")"; 430 } 431 cout << "]" << endl; 432 } 433 } 434 return 0; 435 }
3. 半平面
1 /***************************************************************** 2 半平面交 3 【注意】三角与反三角最多用一次(损失精度大)。 4 【注意】数组下标一律从1开始。 5 *****************************************************************/ 6 7 //#include <bits/stdc++.h> 8 #include <iostream> 9 #include <cstring> 10 #include <cstdlib> 11 #include <iomanip> 12 #include <stack> 13 #include <cstdio> 14 #include <cmath> 15 #include <algorithm> 16 #define re register 17 #define il inline 18 #define ll int 19 #define ld double 20 using namespace std; 21 const ld PI = 3.14159265358979323846; 22 const ll MAXN = 5e5+5; 23 const ld INF = 1e9; 24 const ld EPS = 1e-8; 25 26 //点坐标 27 struct POINT 28 { 29 ld x, y; 30 POINT() : x(0), y(0) {} 31 POINT(ld _x, ld _y) : x(_x), y(_y) {} 32 }; 33 //向量 34 typedef POINT VECTOR; 35 36 //符号函数 37 ll sgn(ld x) {return x < -EPS ? -1 : x > EPS;} 38 //向量+向量=向量,点+向量=点 39 VECTOR operator + (VECTOR a, VECTOR b) {return {a.x+b.x,a.y+b.y};} 40 //向量-向量=向量,点-点=向量 41 VECTOR operator - (VECTOR a, VECTOR b) {return {a.x-b.x,a.y-b.y};} 42 //向量*数=向量 43 VECTOR operator * (VECTOR a, ld k) {return {a.x*k,a.y*k};} 44 VECTOR operator * (ld k, VECTOR a) {return {a.x*k,a.y*k};} 45 //向量/数=向量 46 VECTOR operator / (VECTOR a, ld k) {return {a.x/k,a.y/k};} 47 //向量==向量,点==点 48 bool operator == (const VECTOR a, const VECTOR b) {return !sgn(a.x-b.x) && !sgn(a.y-b.y);} 49 //向量偏序关系(先x轴再y轴) 50 bool operator < (const VECTOR a, const VECTOR b) {return !sgn(a.x-b.x) ? a.y < b.y : a.x < b.x;} 51 //取下端点 52 POINT min(POINT p1, POINT p2) {return p1.y<p2.y ? p1:p2;} 53 //取上端点 54 POINT max(POINT p1, POINT p2) {return p1.y<p2.y ? p2:p1;} 55 //点积 56 ld dot(POINT p1, POINT p2, POINT p) {return (p.x-p1.x)*(p2.x-p1.x)+(p.y-p1.y)*(p2.y-p1.y);} //(三点式:共p1端点) 57 ld dot(VECTOR a, VECTOR b) {return a.x*b.x+a.y*b.y;} //向量式 58 //叉积 59 ld cross(POINT p1, POINT p2, POINT p) {return (p.x-p1.x)*(p2.y-p1.y)-(p.y-p1.y)*(p2.x-p1.x);} //(三点式:共p1端点) 60 ld cross(VECTOR a, VECTOR b) {return a.x*b.y-a.y*b.x;} //向量式(aXb) 61 //向量长度 62 ld vlen(VECTOR a) {return sqrt(dot(a,a));} 63 //向量旋转(逆时针) 64 VECTOR rot(VECTOR a, ld sita) {return {a.x*cos(sita)-a.y*sin(sita),a.x*sin(sita)+a.y*cos(sita)};} 65 //取单位向量 66 VECTOR unit(VECTOR a) {return a/vlen(a);} 67 //取正交单位向量 68 VECTOR norm(VECTOR a) {return VECTOR(-a.y,a.x)/vlen(a);} 69 //向量夹角余弦值 70 ld vcos(VECTOR a, VECTOR b) {return dot(a,b)/(vlen(a)*vlen(b));} 71 //向量夹角正弦值 72 ld vsin(VECTOR a, VECTOR b) {return fabs(cross(a,b))/(vlen(a)*vlen(b));} 73 //向量极角 74 //【注意】使用系统反三角函数返回nan是因为超出了定义域(大多数由于精度造成) 75 //【注意】故使用系统反三角函数需处理边界 76 ld vagl(VECTOR a, VECTOR b) 77 { 78 ld d = dot(a,b)/(vlen(a)*vlen(b)); 79 if(sgn(d-1) == 0) return 0; 80 else if(sgn(d+1) == 0) return PI; 81 else return acos(d); 82 } //向量间[0,PI] 83 ld vagl(VECTOR a) {return atan2(a.y, a.x);} //单向量[-PI,PI] 84 85 //凸包 86 ll vexcnt = 0; //凸包顶点数 87 POINT xy[MAXN], xyt[MAXN]; //多边形顶点存储,临时多边形顶点存储 88 POINT convex[MAXN]; //凸包顶点数组 89 90 //求凸包周长 91 ld convexperimeter(POINT *poly, ll n) 92 { 93 ld ans = 0; 94 for(re ll i = 1; i <= n; ++i) 95 { 96 ans += vlen(poly[i]-poly[i%n+1]); 97 } 98 return ans; 99 } 100 //计算多边形面积 101 ld polygonarea(POINT *poly, ll n) 102 { 103 ld aera = 0; 104 for(re ll i = 0; i < n; ++i) 105 { 106 aera += cross(poly[i+1], poly[(i+1)%n+1]); 107 } 108 return fabs(aera)/2; //不加绝对值为有向面积 109 } 110 111 //andrew算法求凸包(凸包数组正序为逆时针) 112 //1.严格凸包(没有重复点和三点共线)2.非严格凸包(允许重复点和三点共线) 113 void andrew(ll n) 114 { 115 vexcnt = 0; //初始化数据 116 memcpy(xyt,xy,sizeof(xy)); //备份原来顶点集 117 sort(xyt+1,xyt+n+1); //排序后xyt[1]和xyt[n]一定在凸包中 118 //求解凸包顶点集 119 //正向扫描(下凸包) 120 convex[++vexcnt] = xyt[1]; //xyt[1]一定在凸包中 121 ll j = 2; 122 while(j <= n) 123 { 124 if(vexcnt <= 1) //因为xyt[2]不一定在凸包集中 125 { 126 convex[++vexcnt] = xyt[j++]; 127 } 128 else 129 { 130 //取前面两个点 131 //严格凸包:sgn(cross(convex[vexcnt]-convex[vexcnt-1],xyt[j]-convex[vexcnt]))>0 132 //非严格凸包:sgn(cross(convex[vexcnt]-convex[vexcnt-1],xyt[j]-convex[vexcnt]))>=0 133 if(sgn(cross(convex[vexcnt]-convex[vexcnt-1],xyt[j]-convex[vexcnt]))>=0) convex[++vexcnt] = xyt[j++]; 134 else --vexcnt; 135 } 136 } 137 //反向扫描(上凸包) 138 //至少包含了xyt[1]和xyt[n] 139 ll record = vexcnt; //记录下凸包的结果 140 ll k = n-1; 141 while(k >= 1) 142 { 143 if(vexcnt <= record) 144 { 145 convex[++vexcnt] = xyt[k--]; 146 } 147 else 148 { 149 //取前面两个点 150 //严格凸包:sgn(cross(convex[vexcnt]-convex[vexcnt-1],xyt[k]-convex[vexcnt]))>0 151 //非严格凸包:sgn(cross(convex[vexcnt]-convex[vexcnt-1],xyt[k]-convex[vexcnt]))>=0 152 if(sgn(cross(convex[vexcnt]-convex[vexcnt-1],xyt[k]-convex[vexcnt]))>0) convex[++vexcnt] = xyt[k--]; 153 else --vexcnt; 154 } 155 } 156 while(convex[--vexcnt]==convex[1]); //因为和xyt[1]相等的点都在下凸包中了 157 //for(re ll i = 1; i <= vexcnt; ++i) cout << "(" << convex[i].x << "," << convex[i].y << ")" << endl; 158 return; 159 } 160 161 //直线 162 struct LINE 163 { 164 POINT p; //直线上定点 165 VECTOR v; //直线方向向量 166 LINE() {} 167 //点向式 168 LINE(POINT _p, VECTOR _v):p(_p), v(_v) {} 169 //点角式 170 LINE(POINT _p, ld a):p(_p), v(VECTOR(cos(a), sin(a))) {} 171 //通过参数t唯一确定直线上点坐标(点向式:P = p + t*v) 172 POINT point(ld t) {return p + t*v;} 173 //平移向量r 174 LINE trans(VECTOR r) {return LINE(p+r,v);} 175 }; 176 177 //有向直线 178 struct DIRLINE 179 { 180 POINT p; //定点 181 VECTOR v; //方向向量 182 ld a; //极角 183 DIRLINE() {} 184 DIRLINE(POINT _p, VECTOR _v):p(_p),v(_v),a(atan2(v.y,v.x)) {} 185 bool operator < (const DIRLINE &DL) const {return a < DL.a;} 186 }; 187 188 //点到直线距离 189 ld PLdist(POINT p, LINE L) {return fabs(sin(vagl(p-L.p,L.v))*vlen(p-L.p));} 190 //点在有向直线左边 191 bool OnLeft(DIRLINE DL, POINT p) {return sgn(cross(DL.v,p-DL.p)) > 0;} 192 //点在有向直线右边 193 bool OnRight(DIRLINE DL, POINT p) {return sgn(cross(DL.v,p-DL.p)) < 0;} 194 195 //两点中垂线 196 //返回中垂线的个数 197 ll PerpendicularBisector(POINT p1, POINT p2, LINE &L) 198 { 199 if(p1 == p2) return 0; 200 L.p = (p1+p2)/2; 201 L.v = rot(p2-p1, PI/2); 202 return 1; 203 } 204 205 //两直线交点 206 POINT LLitesct; 207 208 //直线与直线的交点 209 //返回交点个数 210 ll LineLineIntersection(LINE L1, LINE L2, POINT &p) 211 { 212 if(sgn(cross(L1.v, L2.v)) == 0) 213 { 214 if(sgn(cross(L2.p-L1.p,L2.v)) == 0) return -1; //两直线重合 215 else return 0; //两直线平行 216 } 217 //以L1为准 218 ld t1 = -cross(L2.p-L1.p, L2.v)/cross(L2.v, L1.v); 219 p = L1.p + t1*L1.v; 220 //以L2为准 221 //ld t2 = -cross(L1.p-L2.p, L1.v)/cross(L1.v, L2.v); 222 //p = L2.p + t2*L2.v; 223 return 1; 224 } 225 ll LineLineIntersection(DIRLINE DL1, DIRLINE DL2, POINT &p) 226 { 227 if(sgn(cross(DL1.v, DL2.v)) == 0) 228 { 229 if(sgn(cross(DL2.p-DL1.p,DL2.v)) == 0) return -1; //两直线重合 230 else return 0; //两直线平行 231 } 232 //以L1为准 233 ld t1 = -cross(DL2.p-DL1.p, DL2.v)/cross(DL2.v, DL1.v); 234 p = DL1.p + t1*DL1.v; 235 //以L2为准 236 //ld t2 = -cross(L1.p-L2.p, L1.v)/cross(L1.v, L2.v); 237 //p = L2.p + t2*L2.v; 238 return 1; 239 } 240 241 //半平面交 242 //返回凸包顶点数 243 //【注意】若半平面交可能是无界区域,则需在外面加一个坐标很大的包围框 244 ll HalfPlaneIntersection(DIRLINE *DL, ll n, POINT *poly) 245 { 246 sort(DL+1,DL+n+1); 247 ll first, last; //双端队列的首尾元素下标 248 DIRLINE *q = new DIRLINE[n+1]; //双端队列 249 POINT *p = new POINT[n+1]; //p[i]为q[i]和q[i+1]的交点 250 first = last = 1; 251 q[1] = DL[1]; //初始化只有一个半平面L[0] 252 for(re ll i = 2; i <= n; ++i) 253 { 254 while(first < last && !OnLeft(DL[i], p[last-1])) --last; 255 while(first < last && !OnLeft(DL[i], p[first])) ++first; 256 q[++last] = DL[i]; 257 if(sgn(cross(q[last].v, q[last-1].v)) == 0) //两向量平行 258 { 259 --last; 260 if(OnLeft(q[last], DL[i].p)) q[last] = DL[i]; //同向取内测的一个 261 } 262 if(first < last) LineLineIntersection(q[last-1],q[last],p[last-1]); 263 } 264 while(first < last && !OnLeft(q[first],p[last-1])) --last; 265 //删除无用平面 266 if(last-first <= 1) return 0; //空集 267 LineLineIntersection(q[last],q[first],p[last]); //计算首尾两个半平面交的交点 268 ll cnt = 0; 269 for(re ll i = first; i <= last; ++i) poly[++cnt] = p[i]; 270 return cnt; 271 } 272 273 int main() 274 { 275 //UVALive-3890 276 ios::sync_with_stdio(false); 277 ll n; 278 //scanf("%lld", &n); 279 while(cin >> n) 280 //while(~scanf("%d", &n)) 281 { 282 if(n == 0) break; 283 POINT *p = new POINT[n+1]; 284 for(re ll i = 1; i <= n; ++i) 285 { 286 cin >> p[i].x >> p[i].y; 287 //scanf("%lf%lf%lf%lf", &p1.x, &p1.y, &p2.x, &p2.y); 288 } 289 VECTOR *v = new VECTOR[n+1]; 290 VECTOR *r = new VECTOR[n+1]; 291 for(re ll i = 1; i <= n; ++i) 292 { 293 v[i] = p[i%n+1]-p[i]; 294 r[i] = norm(v[i]); //单位正交向量 295 } 296 //二分答案 297 ld left = 0, right = 20000; 298 DIRLINE *DL = new DIRLINE[n+1]; 299 POINT *poly = new POINT[n+1]; 300 while(right-left > 1e-6) 301 { 302 ld mid = (left+right)/2; 303 for(re ll i = 1; i <= n; ++i) 304 { 305 DL[i] = DIRLINE(p[i]+r[i]*mid,v[i]); 306 } 307 ll cnt = HalfPlaneIntersection(DL,n,poly); 308 if(!cnt) right = mid; 309 else left = mid; 310 } 311 cout << fixed << setprecision(6) << left << endl; 312 } 313 return 0; 314 }
以上是关于二维几何基础的主要内容,如果未能解决你的问题,请参考以下文章