三维凸包模板
增量算法 复杂度O(n^n)
枚举点集 每次看点与当前所有面的关系 Pr能照到的面删除 剩下的保留搭配np[]
再将交界处的边与Pr的连接面加入np[]
注意需要对数据微小扰动 防止存在四点共面
#include<cstring>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int MaxN = 105;
const double eps = 1e-12;
int n,m;//多面体中n个点 m个边
bool vis[MaxN][MaxN];//vis[i][j]:P在i->j面的上面
double rand_eps(){//随机微小扰动 防止四点共面
return ((double)rand() / RAND_MAX - 0.5) * eps;//[-0.5,0.5]*eps
}
struct Point{
double x,y,z;
void shake(){
x += rand_eps();
y += rand_eps();
z += rand_eps();
}
double len(){
return sqrt(x * x + y * y + z * z);
}
Point operator - (Point t){
return {x - t.x,y - t.y,z - t.z};
}
double operator & (Point t){//点积
return (x*t.x + y*t.y + z*t.z);
}
Point operator * (Point t){//叉积
return {y*t.z - z*t.y, z*t.x - x*t.z, x*t.y - y*t.x};
//AxB = (y1z2 - z1y2, z1x2 - x1z2, x1y2 - x2y1)
}
}p[MaxN];
struct Plane{
int v[3];//构成平面的三个点的序号
Point norm(){//平面法向量 A ×B
return (p[v[1]] - p[v[0]]) * (p[v[2]] - p[v[0]]);
}
double area(){//平面的面积
return norm().len() / 2;//叉积的模长==平行四边形面积
}
bool above(Point a){//点a是否在平面上方
return ((a - p[v[0]]) & norm()) >= 0;//不要再用dcmp啦 不然微小扰动就没意义了
}
}plane[MaxN << 1],np[MaxN << 1];
void get_convex_3d(){
// plane[m++] = {0,1,2};
// plane[m++] = {2,1,0};
plane[m].v[0] = 0,plane[m].v[1] = 1,plane[m].v[2] = 2;
m++;
plane[m].v[0] = 2,plane[m].v[1] = 1,plane[m].v[2] = 0;
m++;
for(int i = 3;i < n; i++){
int cnt = 0;
for(int j = 0;j < m; j++){
bool up = plane[j].above(p[i]);//点i在平面j的上面?
if(!up) np[cnt++] = plane[j];//不在上面 该平面保留
for(int k = 0;k < 3; k++){
vis[plane[j].v[k]][plane[j].v[(k+1)%3]] = up;
}//把顺逆两个平面中a->b b->a的状态存下来 以便找出处于阴暗交接的边
}
for(int j = 0;j < m ;j++){
for(int k = 0;k < 3; k++){
//枚举现在每个平面中的边 确定那些边需要与新点i组成添加进plane[]的面
int a = plane[j].v[k],b = plane[j].v[(k+1)%3];
if(vis[a][b] && !vis[b][a]){
// np[++cnt] = {a,b,i};
np[cnt].v[0] = a,np[cnt].v[1] = b,np[cnt].v[2] = i;
cnt++;
}
}
}
m = cnt;
for(int j = 0;j < m; j++){
plane[j] = np[j];
}
}
}
int main()
{
scanf("%d",&n);
for(int i = 0;i < n; i++){
scanf("%lf %lf %lf",&p[i].x,&p[i].y,&p[i].z);
p[i].shake();
}
get_convex_3d();//三维凸包
double ans = 0.0;
for(int i = 0;i < m; i++){
ans += plane[i].area();
}
printf("%.6f\n",ans);
}