【模板】【计几】三维凸包
两道模板题
求三维凸包表面积:
求三维凸包表面多少个多边形:
1 #include<bits/stdc++.h> 2 using namespace std; 3 #define eps 1e-8 4 const int N = 2e3+9; 5 struct Point3{ 6 double x,y,z; 7 Point3(){} 8 Point3(double _x,double _y,double _z) : x(_x),y(_y),z(_z){} 9 Point3 operator + (Point3 b){ return Point3(x+b.x,y+b.y,z+b.z); } 10 Point3 operator - (Point3 b){ return Point3(x-b.x,y-b.y,z-b.z); } 11 Point3 operator * (double k){ return Point3(x*k,y*k,z*k); } 12 }; 13 #define Vector3 Point3 14 double dot(Vector3 a,Vector3 b){return a.x*b.x + a.y*b.y + a.z*b.z;} 15 Point3 cross(Vector3 a,Vector3 b){ 16 return Point3(a.y*b.z-a.z*b.y , a.z*b.x-a.x*b.z , a.x*b.y-a.y*b.x); 17 } 18 double len(Vector3 a){return sqrt(dot(a,a)); } 19 //面积的2倍 20 double area2(Point3 a,Point3 b,Point3 c){ return len(cross(b-a,c-a)); } 21 //四面体体积*6 22 double volume4(Point3 a,Point3 b,Point3 c,Point3 d){ 23 return dot( cross(b-a,c-a) , d-a ); 24 } 25 struct CH3D{ 26 struct face{ 27 //三个点编号,逆时针 28 int a,b,c; 29 //是否在最终凸包上 30 bool ok; 31 }; 32 //n点数(0开始),cnt面数(1开始),face是凸包表面,都是三角形,g[x][y]记录x到y这个向量右边的面编号 33 int n,cnt; Point3 P[N]; face F[2*N]; int g[N][N]; 34 //判断点和面的法向量是否同向 35 double dblcmp(Point3 p,face f){ 36 Point3 m = P[f.b] - P[f.a]; 37 Point3 n = P[f.c] - P[f.a]; 38 Point3 t = p - P[f.a]; 39 return dot(cross(m,n),t); 40 } 41 //一直往下删除 42 void dfs(int p,int a,int b){ 43 int f = g[a][b]; 44 if(F[f].ok == 0 ) return; 45 if(dblcmp(P[p],F[f]) > eps){ 46 //如果p能看到面f,那么把f删除 47 F[f].ok = 0; 48 dfs(p,F[f].b,F[f].a); 49 dfs(p,F[f].c,F[f].b); 50 dfs(p,F[f].a,F[f].c); 51 } 52 else{ 53 //看不到则add一个面 54 face add; 55 add.a = b; add.b = a; 56 add.c = p; add.ok = 1; 57 ++cnt; 58 g[p][b] = g[a][p] = g[b][a] = cnt; 59 F[cnt] = add; 60 } 61 62 } 63 void del(int p,int now){ 64 //删除凸包面 65 F[now].ok = 0; 66 dfs(p,F[now].b,F[now].a); 67 dfs(p,F[now].c,F[now].b); 68 dfs(p,F[now].a,F[now].c); 69 } 70 void create(){ 71 cnt = 0; 72 memset(g,0,sizeof(g)); 73 if(n<4) return; 74 bool find = 0; 75 //前两个点不共点 76 for(int i = 1;i<n;++i){ 77 if(len(P[0] - P[i]) > eps){ 78 swap(P[1],P[i]); 79 find = 1; 80 break; 81 } 82 } 83 if(!find) return; 84 find = 0; 85 //前三个点不共线 86 for(int i = 2;i<n;++i){ 87 if(len( cross(P[0] - P[1] , P[1]- P[i]) ) > eps){ 88 swap(P[2],P[i]); 89 find = 1; 90 } 91 } 92 if(!find ) return; 93 find = 0; 94 //前四个点不共面 95 for(int i = 3;i<n;++i){ 96 if( fabs( dot( cross(P[0]-P[1],P[1]-P[2]),P[0]-P[i] )) > eps){ 97 swap(P[3],P[i]); 98 find = 1; 99 break; 100 } 101 } 102 if(!find) return; 103 for(int i = 0;i<4;++i){ 104 face add; 105 add.a = (i+1)%4; 106 add.b = (i+2)%4; 107 add.c = (i+3)%4; 108 add.ok = 1; 109 //保证逆时针,就是法向量朝外,这样新点才可看到 110 if(dblcmp(P[i],add) > 0 ) swap(add.b,add.c); 111 ++cnt; 112 g[add.a][add.b] = g[add.b][add.c] = g[add.c][add.a] = cnt; 113 F[cnt] = add; 114 } 115 for(int i = 4;i<n;++i){ 116 for(int j = 1;j<=cnt;++j){ 117 //i点,j面 118 if(F[j].ok && dblcmp(P[i],F[j]) > eps){ 119 del(i,j); 120 break; 121 } 122 } 123 } 124 int tem = cnt; 125 cnt = 0; 126 for(int i = 1;i<=tem;++i){ 127 if(F[i].ok) F[++cnt] = F[i]; 128 } 129 } 130 //凸包表面积 131 double area(){ 132 double res = 0; 133 for(int i = 1;i<=cnt;++i){ 134 res += area2(P[F[i].a],P[F[i].b],P[F[i].c]); 135 } 136 return res/2.0; 137 } 138 //凸包体积 139 double volume(){ 140 double res = 0; 141 Point3 tem(0,0,0); 142 for(int i = 1;i<=cnt;++i) res += volume4(tem,P[F[i].a],P[F[i].b],P[F[i].c]); 143 return fabs(res/6.0); 144 } 145 //判断两面是否同一个面 146 bool same(int s,int t){ 147 Point3 a = P[F[s].a]; 148 Point3 b = P[F[s].b]; 149 Point3 c = P[F[s].c]; 150 return fabs( volume4(a,b,c,P[F[t].a])) < eps && 151 fabs( volume4(a,b,c,P[F[t].b])) < eps && 152 fabs( volume4(a,b,c,P[F[t].c])) < eps; 153 } 154 //表面多边形个数 155 int polygon(){ 156 int res = 0; 157 for(int i = 1;i<=cnt;++i){ 158 bool ok = 1; 159 for(int j = 1;j<i;++j){ 160 if(same(i,j)){ 161 ok = 0; 162 break; 163 } 164 } 165 res += ok; 166 } 167 return res; 168 } 169 }; 170 CH3D hull; 171 int main(){ 172 while(~scanf("%d",&hull.n)){ 173 for(int i = 0;i<hull.n;++i){ 174 scanf("%lf %lf %lf",&hull.P[i].x,&hull.P[i].y,&hull.P[i].z); 175 } 176 hull.create(); 177 printf("%.3f\n",hull.area()); 178 // printf("%d\n",hull.polygon()); 179 } 180 return 0; 181 }