计算几何板子很重要能避免很多精度误差
点 直线 和多边形 具体理解了
线段部分直接抄过来了
(~~太痛苦了 计算几何 ~~)
constexpr double eps = 1e-8;
constexpr double PI = 3.1415926535897932384l;
int dcmp(double x) {
//三态函数,克服浮点数精度陷阱,判断x==0?x<0?x>0?
if (fabs(x) < eps)
return 0;
else
return x < 0 ? -1 : 1;
}
template<typename T>struct point { // 点类
T x, y;
// 关系运算符
bool operator == (const point &a) const {
return (abs(x - a.x) <= eps && abs(y - a.y) <= eps);
}
bool operator < (const point &a) const {
if (abs(x - a.x) <= eps) return y < a.y;
return x < a.x;
}
// 向量的模长、单位化向量
T length2() const {
return (*this) * (*this);
}
T length() const {
return sqrt(length2());
}
point unit() {
double len = length();
return {x / len, y / len};
}
// 向量的基本运算
point operator -() const {
return { -x, -y};
}
point operator + (const point &a) const {
return {x + a.x, y + a.y};
}
point operator - (const point &a) const {
return {x - a.x, y - a.y};
}
point operator * (const T k) const {
return {k * x, k * y};
}
point operator / (const T k) const {
return {x / k, y / k};
}
// 向量的点乘、叉乘
T operator * (const point &a) const {
return x * a.x + y * a.y; //Dot
}
T operator ^ (const point &a) const {
return x * a.y - y * a.x; //Cross
}
// 逆时针旋转向量、获取向量与x轴正方向形成的夹角
point rotate(const double rad) const {
double c = cos(rad), s = sin(rad);
return {x*c - y * s, x*s + y * c};
}
point rotate90() const {
return { -y, x};
}
double get_angle (const point &a) const {
return atan2(y, x);
}
// 重载输入输出运算符
friend istream & operator >> (istream&, point &a) {
scanf("%lf %lf", &a.x, &a.y);
return cin;
}
friend ostream & operator << (ostream&, point &a) {
printf(" ( %.2lf , %.2lf ) ", a.x, a.y);
return cout;
}
// to-left测试
int toleft (const point &a) const {
const auto t = (*this)^a;
return (t > eps) - (t < -eps);
}
};
double dist2(const point<double> a, const point<double> b) {
return (a - b).length2();
}
double dist (const point<double> a, const point<double> b) {
return sqrt(dist2(a, b));
}
template<typename T> struct line { // 直线
point<T> p, v;
bool operator == (const line &a) const {
return abs(v ^ a.v) <= eps && abs(v ^ (p - a.p)) <= eps;
}
int toleft(const point<T> &a) const {
return v.toleft(a - p);
}
};
//to—left测试 1左边 0 线上 -1 右边
int to_left(const line<double> &l, const point<double> &p) {
return l.v.toleft(p - l.p);
}
//点到直线的距离
double dist(const line<double> &l, const point<double> &p) {
return abs(l.v ^ (p - l.p)) / l.v.length();
}
//点在直线的投影
point<double> projection(const line<double> &l, const point<double> &p) {
return l.p + l.v * ((l.v * (p - l.p)) / (l.v * l.v));
}
//两直线交点
point<double> intersection(const line<double> &a, const line<double> &b) {
return a.p + a.v * ((b.v ^ (a.p - b.p)) / (a.v ^ b.v));
}
//多边形
template<typename T> struct polygon {
vector<point<T>> p;//逆时针存储
// 获取后/前一个点
inline size_t nxt(const size_t i) const {
return i == p.size() - 1 ? 0 : i + 1;
}
inline size_t pre(const size_t i) const {
return i == 0 ? p.size() - 1 : i - 1;
}
//回转数
// 返回值第一项表示点是否在多边形边上
// 对于狭义多边形,回转数为 0 表示点在多边形外,否则点在多边形内
pair<bool, int> winding(const point<T> &a) const {
int cnt = 0;
for (size_t i = 0; i < p.size(); i++) {
point<T> u = p[i], v = p[nxt(i)];
if (abs((a - u) ^ (a - v)) <= eps && (a - u) * (a - v) <= eps) return {true, 0}; // 点a在线段uv上
if (abs(u.y - v.y) <= eps) continue;
line<T> uv = {u, v - u};
if (u.y < v.y - eps && uv.toleft(a) <= 0) continue; // 方向向上且不在左侧
if (u.y > v.y + eps && uv.toleft(a) >= 0) continue; // 方向向上且不在右侧
if (u.y < a.y - eps && v.y >= a.y - eps) cnt++; // 向上穿过
if (u.y >= a.y - eps && v.y < a.y - eps) cnt--; // 向下穿过
}
return {false, cnt};
}
// 计算周长
double perimeter() const {
double sum = 0;
for (size_t i = 0; i < p.size(); i++) sum += dist(p[i], p[nxt(i)]);
return sum;
}
// 计算面积
double area() const {
double sum = 0;
for (size_t i = 0; i < p.size(); i++) sum += p[i] ^ p[nxt(i)];
return abs(sum) / 2;
}
};
// 线段
template<typename T> struct segment {
point<T> a, b;
// 判定性函数建议在整数域使用
// 判断点是否在线段上
// -1 点在线段端点 | 0 点不在线段上 | 1 点严格在线段上
int is_on(const point<T> &p) const {
if (p == a || p == b) return -1;
return (p - a).toleft(p - b) == 0 && (p - a) * (p - b) < -eps;
}
// 判断线段直线是否相交
// -1 直线经过线段端点 | 0 线段和直线不相交 | 1 线段和直线严格相交
int is_inter(const line<T> &l) const {
if (l.toleft(a) == 0 || l.toleft(b) == 0) return -1;
return l.toleft(a) != l.toleft(b);
}
// 判断两线段是否相交
// -1 在某一线段端点处相交 | 0 两线段不相交 | 1 两线段严格相交
int is_inter(const segment<T> &s) const {
if (is_on(s.a) || is_on(s.b) || s.is_on(a) || s.is_on(b)) return -1;
const line<T> l {a, b - a}, ls {s.a, s.b - s.a};
return l.toleft(s.a) * l.toleft(s.b) == -1 && ls.toleft(a) * ls.toleft(b) == -1;
}
// 点到线段距离
long double dis(const point<T> &p) const {
if ((p - a) * (b - a) < -eps || (p - b) * (a - b) < -eps) return min(p.dis(a), p.dis(b));
const line<T> l {a, b - a};
return l.dis(p);
}
// 两线段间距离
long double dis(const segment<T> &s) const {
if (is_inter(s)) return 0;
return min({dis(s.a), dis(s.b), s.dis(a), s.dis(b)});
}
};