转自:http://bbs.byr.cn/#!article/ACM_ICPC/63440
/*
Project: AkemiHomura's Standard Code Library [AHSCL]
Author: [BUPT]AkemiHomura@UESTC_Kyouko
Category: Geometry
Title: Geometry Base 3d ver Header
Version: 0.314
Date: 2012-08-26
Remark: Structure definations and base functions
Tested Problems: N/A
Tag: Geometry
Special Thanks: Amber, eucho@PuellaMagi, mabodx@PuellaMagi, lxyxynt@UESTC_Kyouko, thomas0726@UESTC_Kyouko
*/
#ifndef GEOMETRY_BASE_3D_H
#define GEOMETRY_BASE_3D_H
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
/***<常量表****/
const int MAXN = 1000;
const double INF = 1e10;
const double EPS = 1e-8;
const double PI = acos(-1.0);
/****常量表>***/
/***<浮点函数****/
inline int sgn(double d) {
if (fabs(d) < EPS) return 0;
return d>0 ? 1: -1;
}
inline double min(double a, double b)
{return a<b ? a : b;}
inline double max(double a, double b)
{return a>b ? a : b;}
inline double sqr(double x) {
return x*x;
}
/****浮点函数>***/
/***<点/向量定义与运算****/
struct sp {
double x, y, z;
sp() {}
sp(double a, double b, double c): x(a), y(b), z(c) {}
bool read() {return scanf("%lf%lf%lf", &x, &y, &z)==3;}
void write() {printf("%f %f %f\n", x, y, z);}
};
sp operator-(const sp &u, const sp &v) {
return sp(u.x-v.x, u.y-v.y, u.z-v.z);
}
sp operator+(const sp &u, const sp &v) {
return sp(u.x+v.x, u.y+v.y, u.z+v.z);
}
//数乘
sp operator*(double d, const sp &v) {
return sp(d*v.x, d*v.y, d*v.z);
}
//范数
double nrm(const sp &v) {
return sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
}
//范数平方
double nrmsqr(const sp &v) {
return v.x*v.x+v.y*v.y+v.z*v.z;
}
//内积(点积)
double dot(const sp &u, const sp &v) {
return u.x*v.x+u.y*v.y+u.z*v.z;
}
//外积(叉积)
sp det(const sp &u, const sp &v) {
return sp(u.y*v.z-v.y*u.z,
u.z*v.x-u.x*v.z,
u.x*v.y-u.y*v.x);
}
//三角形有向面积*2
//(p, u, v)确定的右手系
sp dir(const sp &p, const sp &u, const sp &v) {
return det(u-p, v-p);
}
//三角形面积*2
double area(const sp &a, const sp &b, const sp &c) {
return nrm(dir(a, b, c));
}
//四面体有向体积*6
//正负代表p在(v-u, w-u)所确定右手系平面的方向
double volume(const sp &u, const sp &v, const sp &w, const sp &p) {
return dot(dir(u, v, w), p-u);
}
const sp ORIGIN = sp(0, 0, 0);
/****点/向量定义与运算>***/
/***<线段/直线定义****/
struct ss
{
sp a, b;
ss() {}
ss(sp u, sp v): a(u), b(v) {}
};
/****线段/直线定义>***/
/***<平面定义****/
struct splane
{
sp a, b, c;
splane() {}
splane(sp u, sp v, sp w): a(u), b(v), c(w) {}
};
//法向量
sp nv(const splane &s) {
return det(s.a-s.b, s.b-s.c);
}
//ax+by+cz+d=0
struct splanef {
double a, b, c, d;
splanef() {}
splanef(double p, double q, double r, double s): a(p), b(q), c(r), d(s) {}
splanef(const splane &p) {
sp m = det(p.b-p.a, p.c-p.a);
a = m.x, b = m.y, c = m.z;
d = -m.x*p.a.x-m.y*p.a.y-m.z*p.a.z;
}
};
/****平面定义>***/
/***<点与线基本函数****/
//两点距离
double dis(const sp &u, const sp &v) {
return sqrt(sqr(u.x-v.x)+sqr(u.y-v.y)+sqr(u.z-v.z));
}
//三点共线
bool coline(const sp &p1, const sp &p2, const sp &p3) {
return nrmsqr(det(p1-p2,p2-p3))<EPS;
}
//四点共面
bool coplane(const sp &a, const sp &b, const sp &c, const sp &d) {
return sgn(dot(det(c-a, b-a), d-a))==0;
}
//点在直线上
bool online(const sp &p, const ss &l) {
return coline(p, l.a, l.b);
}
//点在线段上,包括端点
bool onseg(const sp &p, const ss &l) {
return sgn(nrmsqr(det(p-l.a, p-l.b)))==0 &&
(l.a.x-p.x)*(l.b.x-p.x)<EPS &&
(l.a.y-p.y)*(l.b.y-p.y)<EPS &&
(l.a.z-p.z)*(l.b.z-p.z)<EPS;
}
//点在线段上,不包括端点
bool onsegex(const sp &p, const ss &l) {
return onseg(p, l) &&
(sgn(p.x-l.a.x)||sgn(p.y-l.a.y)||sgn(p.z-l.a.z)) &&
(sgn(p.x-l.b.x)||sgn(p.y-l.b.y)||sgn(p.z-l.b.z));
}
//点在空间三角形上,包括边界,三点共线无意义
bool onplane(const sp &p, const splane &s) {
return sgn(nrm(det(s.a-s.b, s.a-s.c))-
nrm(det(p-s.a, p-s.b))-
nrm(det(p-s.b, p-s.c))-
nrm(det(p-s.c, p-s.a)))==0;
}
//点在空间三角形上,不包括边界,三点共线无意义
bool onplaneex(const sp &p, const splane &s) {
return onplane(p, s) &&
nrmsqr(det(p-s.a, p-s.b))>EPS &&
nrmsqr(det(p-s.b, p-s.c))>EPS &&
nrmsqr(det(p-s.c, p-s.a))>EPS;
}
//两点在直线同侧,点在线段上返回0,不共面无意义
bool sameside(const sp &u, const sp &v, const ss &l) {
return dot(det(l.a-l.b, p-l.b),det(l.a-l.b, q-l.b))>EPS;
}
//两点在直线异侧,点在线段上返回0,不共面无意义
bool oppositeside(const sp &u, const sp &v, const ss &l) {
return dot(det(l.a-l.b, p-l.b),det(l.a-l.b, q-l.b))<-EPS;
}
//两点在平面同侧,点在平面上返回0
bool sameside(const sp &u, const sp &v, const splane &s) {
return dot(nv(s), det(p-s.a))*dot(nv(s),det(q-s.a))>EPS;
}
//两点在平面同侧,点在平面上返回0
bool sameside(const sp &u, const sp &v, const splanef &s) {
return (s.a*u.x+s.b*u.y+s.c*u.z+s.d)*(s.a*v.x+s.b*v.y+s.c*v.z+s.d)>EPS;
}
//判两点在平面异侧,点在平面上返回0
bool oppositeside(const sp &u, const sp &v, const splane &s) {
return dot(nv(s), det(p-s.a))*dot(nv(s),det(q-s.a))<-EPS;
}
//判两点在平面异侧,点在平面上返回0
bool oppositeside(const sp &u, const sp &v, const splanef &s) {
return (s.a*u.x+s.b*u.y+s.c*u.z+s.d)*(s.a*v.x+s.b*v.y+s.c*v.z+s.d)<-EPS;
}
//判两直线平行
bool parallel(const ss &u, const ss &v) {
return nrmsqr(det(u.a-u.b, v.a-v.b))<EPS;
}
//判两平面平行
bool parallel(const splane &u, const splane &v) {
return nrmsqr(det(nv(u), nv(v)))<EPS;
}
//点到直线距离(直线不可以退化)
double disptoline(const sp &p, const ss &l) {
return nrm(det(p-l.a, l.b-l.a))/dis(l.a, l.b);
}
//点到直线最近点
sp ptoline(const sp &p, const ss &l) {
sp ab = l.b-l.a;
double t = -dot(p-l.a, ab)/nrmsqr(ab);
ab = t*ab;
return l.a-ab;
}
//点到线段距离
double disptoseg(const sp &p, const ss &l) {
sp q = ptoline(p, l);
if (onseg(q, l)) return dis(p, q);
return min(dis(p, l.a), dis(p, l.b));
}
//点到线段最近点
sp ptoseg(const sp &p, const ss &l) {
sp q = ptoline(p, l);
if (onseg(q, l)) return q;
return dis(p, l.a)<dis(p, l.b)?l.a:l.b;
}
//直线到直线最短距离
double dislinetoline(const ss &u, const ss &v) {
sp n = det(u.a-u.b, v.a-v.b);
if (nrmsqr(n)<EPS) //两直线平行
return disptoline(u.a, v);
else
return fabs(dot(u.a-v.a, n))/nrm(n);
}
//直线到直线最近点对(需要保证直线不平行)
//返回p在u上,q在v上
void linetoline(const ss &u, const ss &v, sp &p, sp &q) {
//assert(!parallel(u, v));
sp ab = u.b-u.a, cd = v.b-v.a, ac = v.a-u.a;
double r = (dot(ab, cd)*dot(ac, ab)-nrmsqr(ab)*dot(ac, cd)) /
(nrmsqr(ab)*nrmsqr(cd)-sqr(dot(ab, cd)));
q = v.a+(r*cd);
p = ptoline(q, u);
}
//线段到线段最短距离
double dissegtoseg(const ss &u, const ss &v) {
double res = INF;
res = min(res, disptoseg(u.a, v));
res = min(res, disptoseg(u.b, v));
res = min(res, disptoseg(v.a, u));
res = min(res, disptoseg(v.b, u));
if (!parallel(u, v)) {
sp p, q;
linetoline(u, v, p, q);
if (onseg(p, u) && onseg(q, v))
res = min(res, dis(p, q));
}
return res;
}
#endif
三维凸包(随机增量法)
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int MAX = 1111;
const double EPS = 1e-8;
inline int sgn(double d) {
if (fabs(d) < EPS) return 0;
return d>0 ? 1: -1;
}
struct sp {
double x, y, z;
sp() {}
sp(double a, double b, double c): x(a), y(b), z(c) {}
bool read() {return scanf("%lf%lf%lf", &x, &y, &z)==3;}
};
sp operator-(const sp &u, const sp &v) {
return sp(u.x-v.x, u.y-v.y, u.z-v.z);
}
double dot(const sp &u, const sp &v) {
return u.x*v.x+u.y*v.y+u.z*v.z;
}
sp det(const sp &u, const sp &v) {
return sp(u.y*v.z-v.y*u.z,
u.z*v.x-u.x*v.z,
u.x*v.y-u.y*v.x);
}
sp dir(const sp &p, const sp &u, const sp &v) {
return det(u-p, v-p);
}
double nrm(const sp &v) {
return sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
}
//三角形面积*2
double area(const sp &a, const sp &b, const sp &c) {
return nrm(dir(a, b, c));
}
//四面体有向体积*6
//正负代表p在(v-u, w-u)所确定右手系平面的方向
double volume(const sp &u, const sp &v, const sp &w, const sp &p) {
return dot(dir(u, v, w), p-u);
}
//四点共面(计算凸包有多少面时需要)
bool coplane(const sp &a, const sp &b, const sp &c, const sp &d) {
return sgn(dot(det(c-a, b-a), d-a))==0;
}
//三维凸包的表面三角形
struct sf {
int a, b, c;//三个顶点编号
bool ok;//面是否最终是凸包上的面
sf() {}
sf(int p, int q, int r, bool b): a(p), b(q), c(r), ok(b) {}
};
struct sch3d {
int N;//初始点数
sp p[MAX];//初始点,注意:点顺序将被打乱!
int cnt;//凸包所有面数
sf f[MAX];//凸包所有面
int to[MAX][MAX];//标记数组
double dir(const sf &t, const sp &u) {
return ::volume(p[t.a], p[t.b], p[t.c], u);
}
void deal(int t, int a, int b) {
int fid = to[a][b];
if (f[fid].ok) {
if (dir(f[fid], p[t]) > EPS) dfs(t, fid);
else {
to[t][b] = to[a][t] = to[b][a] = cnt;
f[cnt++] = sf(b, a, t, 1);
}
}
}
void dfs(int t, int cur) {
f[cur].ok = 0;
deal(t, f[cur].b, f[cur].a);
deal(t, f[cur].c, f[cur].b);
deal(t, f[cur].a, f[cur].c);
}
//构建三维凸包
void ch() {
cnt = 0;
if (N < 4) return;
int i, j, k;
//保证前四个点不共面
while (1) {
random_shuffle(p, p+N);
if (!::coplane(p[0], p[1], p[2], p[3])) break;
}
sf now;
for (i = 0; i < 4; ++i) {
now = sf(i+1&3, i+2&3, i+3&3, 1);
if (dir(now, p[i]) > 0) swap(now.b, now.c);
to[now.a][now.b] = to[now.b][now.c] = to[now.c][now.a] = cnt;
f[cnt++] = now;
}
for (i = 4; i < N; ++i)
for (j = 0; j < cnt; ++j)
if (f[j].ok && dir(f[j], p[i])>EPS) {
dfs(i, j);
break;
}
int tmp = cnt;
cnt = 0;
for (i = 0; i < tmp; ++i)
if (f[i].ok) f[cnt++] = f[i];
}
//表面积
double area() {
double res = 0;
for (int i = 0; i < cnt; ++i)
res += ::area(p[f[i].a], p[f[i].b], p[f[i].c]);
return res/2;
}
//体积
double volume() {
sp o = sp(0, 0, 0);
double res = 0;
for (int i = 0; i < cnt; ++i)
res += ::volume(o, p[f[i].a], p[f[i].b], p[f[i].c]);
return fabs(res/6);
}
//三角形u,v共面
bool same(int u, int v) {
sp a = p[f[u].a], b = p[f[u].b], c = p[f[u].c];
return ::coplane(a, b, c, p[f[v].a]) &&
::coplane(a, b, c, p[f[v].b]) &&
::coplane(a, b, c, p[f[v].c]);
}
//凸包面数
int facecnt() {
int res = 0;
for (int i = 0; i < cnt; ++i) {
bool t = 1;
for (int j = 0; j < i; ++j) {
if (same(i, j)) {
t = 0;
break;
}
}
res += t;
}
return res;
}
}ch3d;