三维凸包
#include <cstdio>
#include <cmath>
#include <cstring>
#include <vector>
#include <algorithm>
const int MAXN = 505;
const double EPS = 1e-9;
int dcmp(double a, double b = 0) {
double d = a - b;
return std::abs(d) <= EPS ? 0 : (d > 0 ? 1 : -1);
}
double asqrt(double x) { return x <= EPS ? 0 : std::sqrt(x); }
double sqr(double x) { return x * x; }
struct Point {
double x, y, z;
Point(double x = 0, double y = 0, double z = 0) : x(x), y(y), z(z) {}
Point operator+(const Point &rhs) const {
return Point(x + rhs.x, y + rhs.y, z + rhs.z);
}
Point operator-(const Point &rhs) const {
return Point(x - rhs.x, y - rhs.y, z - rhs.z);
}
bool operator==(const Point &rhs) const {
return !dcmp(x, rhs.x) && !dcmp(y, rhs.y) && !dcmp(z, rhs.z);
}
bool operator<(const Point &rhs) const {
if (dcmp(x, rhs.x)) return x < rhs.x;
if (dcmp(y, rhs.y)) return y < rhs.y;
return z < rhs.z;
}
friend double dot(const Point &a, const Point &b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
friend Point cross(const Point &a, const Point &b) {
return Point(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x);
}
friend double mix(const Point &a, const Point &b, const Point &c) {
return dot(a, cross(b, c));
}
double length() const {
return std::sqrt(sqr(x) + sqr(y) + sqr(z));
}
} P[MAXN];
double volumn(const Point &a, const Point &b, const Point &c, const Point &d) {
return mix(b - a, c - a, d - a) / 6.0;
}
bool doesPointOnLine(const Point &p, const Point &s, const Point &t) {
return dcmp(cross(p - s, t - s).length()) == 0;
}
namespace ConvexHull3D {
struct Face {
int a, b, c;
bool judged;
Face(int a = 0, int b = 0, int c = 0) : a(a), b(b), c(c), judged(false) {}
int &operator[](int i) {
return i == 0 ? a : (i == 1 ? b : c);
}
const int &operator[](int i) const {
return i == 0 ? a : (i == 1 ? b : c);
}
};
std::vector<Face> ch;
bool same(int a, int b) {
return !dcmp(volumn(P[ch[a][0]], P[ch[a][1]], P[ch[a][2]], P[ch[b][0]]))
&& !dcmp(volumn(P[ch[a][0]], P[ch[a][1]], P[ch[a][2]], P[ch[b][1]]))
&& !dcmp(volumn(P[ch[a][0]], P[ch[a][1]], P[ch[a][2]], P[ch[b][2]]));
}
bool doesFaceHaveEdge(const Face &f, int s, int t) {
for (int i = 0; i < 3; i++) {
if (f[i] == s && f[(i + 1) % 3] == t) return true;
if (f[i] == t && f[(i + 1) % 3] == s) return true;
}
return false;
}
bool find(int n) {
for (int i = 2; i < n; i++) {
if (doesPointOnLine(P[i], P[0], P[1])) continue;
std::swap(P[2], P[i]);
for (int j = i + 1; j < n; j++) {
if (dcmp(volumn(P[0], P[1], P[2], P[j]))) {
std::swap(P[3], P[j]);
ch.emplace_back(0, 1, 2);
ch.emplace_back(0, 2, 1);
return true;
}
}
}
return false;
}
int mark[MAXN][MAXN];
void add(int pi, int &cnt) {
static std::vector<Face> temp;
temp.clear();
++cnt;
for (auto f : ch) {
int a = f[0], b = f[1], c = f[2];
if (dcmp(volumn(P[pi], P[a], P[b], P[c])) < 0) {
mark[a][b] = mark[b][a] = cnt;
mark[a][c] = mark[c][a] = cnt;
mark[b][c] = mark[c][b] = cnt;
} else {
temp.push_back(f);
}
}
ch = temp;
for (auto f : temp) {
int a = f[0], b = f[1], c = f[2];
if (mark[a][b] == cnt) ch.emplace_back(b, a, pi);
if (mark[b][c] == cnt) ch.emplace_back(c, b, pi);
if (mark[c][a] == cnt) ch.emplace_back(a, c, pi);
}
}
bool getConvexHull(int n) {
std::sort(P, P + n);
n = std::unique(P, P + n) - P;
std::random_shuffle(P, P + n);
if (find(n)) {
memset(mark, 0, sizeof (mark));
int cnt = 0;
for (int i = 3; i < n; i++) add(i, cnt);
} else {
return false;
}
int F = ch.size(), V = (4 + F) / 2, E = V + F - 2;
int FF = 0, EE = E;
for (int i = 0; i < ch.size(); i++) if (!ch[i].judged) {
static std::vector<Face> f;
f.clear();
for (int j = 0; j < ch.size(); j++) if (same(i, j) && !ch[j].judged) {
f.push_back(ch[j]);
ch[j].judged = true;
}
for (int j = 0; j < f.size(); j++) for (int k = 0; k < 3; k++) for (int l = 0; l < j; l++)
if (doesFaceHaveEdge(f[l], f[j][k], f[j][(k + 1) % 3])) {
--EE;
break;
}
++FF;
}
int VV = EE + 2 - FF;
double area = 0;
for (auto f : ch) {
Point p = cross(P[f[0]] - P[f[1]], P[f[2]] - P[f[1]]);
area += p.length() / 2.0;
}
double vol = 0;
const Point &vp = P[ch[0][0]];
for (auto f : ch) vol += std::abs(volumn(vp, P[f[0]], P[f[1]], P[f[2]]));
return true;
}
}
int main() {
int n;
scanf("%d", &n);
for (int i = 0; i < n; i++) scanf("%lf %lf %lf", &P[i].x, &P[i].y, &P[i].z);
static bool banned[MAXN];
for (int i = 0; i < n; i++) if (!banned[i]) for (int j = 0; j < i; j++) if (!banned[j]) {
static std::vector<std::pair<Point, int> > l;
l.clear();
for (int k = 0; k < n; k++) if (!banned[k] && doesPointOnLine(P[k], P[i], P[j]))
l.emplace_back(P[k], k);
std::sort(l.begin(), l.end());
for (int k = 1; k < l.size() - 1; k++) banned[l[k].second] = true;
}
int p = 0;
for (int i = 0; i < n; i++) if (!banned[i]) P[p++] = P[i];
ConvexHull3D::getConvexHull(p);
return 0;
}