计算几何 入门 (存个板子)

计算几何板子很重要能避免很多精度误差
点 直线 和多边形 具体理解了
线段部分直接抄过来了
(~~太痛苦了 计算几何 ~~)


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)});
	}
};





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值