【模板】【计几】三维凸包

两道模板题

求三维凸包表面积:

洛谷p4724

求三维凸包表面多少个多边形:

hdu3662

 

  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 }
View Code

 

posted @ 2020-03-27 19:44  小布鞋  阅读(202)  评论(0编辑  收藏  举报