计算几何

人一定要有梦想。

精度处理 #

精度设置 #

精度的最小误差 eps 通常在 $10^{-8}$ 以内,视题目要求改变。

using var = double;
const var eps = 1e-8;

符号函数 sign #

$$\DeclareMathOperator{\sign}{sign} \DeclareMathOperator{\eps }{eps} \sign(x)= \begin{cases} -1,&x<-\eps\\ 0,&-\eps\le x\le \eps\\ 1,&x>\eps \end{cases}$$

inline int sign(var x) {
	return x < -eps ? -1 : (x > eps ? 1 : 0);
}

绝对值函数 absolute #

$$\DeclareMathOperator{\absolute}{absolute} \absolute(x)=x\cdot\sign(x)$$

inline var absolute(var x) {
	return x * sign(x);
}

符号约定 #

Point #

二维平面点由 $x,y$ 坐标组成。

struct Point {
	var x, y;
	Point(var x = 0, var y = 0)
		: x(x), y(y) {}
};

在精度范围内判断相等。

bool operator == (const Point& a, const Point& b) {
	return ! sign(a.x - b.x) && ! sign(a.y - b.y);
}

标准输入输出支持。

istream& operator >> (istream& in, Point& p) {
	in >> p.x >> p.y;
	return in;
}

ostream& operator << (ostream& out, const Point& p) {
	out << "(" << p.x << ", " << p.y << ")";
	return out;
}

向量 Vector #

向量 Vector 的定义沿用 Point 的定义。

using Vector = Point;

多边形 Poly #

多边形 Poly 由一组 Point 构成。

using Poly = vector<Point>;

标准输入输出支持。

istream& operator >> (istream& in, Poly& poly) {
	for (auto& p : poly) {
		in >> p;
	}
	return in;
}

ostream& operator << (ostream& out, Poly& poly) {
	for (const auto& p : poly) {
		out << p << ",";
	}
	return out;
}

向量运算 #

加减 operator +/- #

$$(x_1,y_1)\pm(x_2,y_2)=(x_1\pm x_2,y_1\pm y_2)$$

Vector operator + (const Vector& a, const Vector& b) {
	return { a.x + b.x, a.y + b.y };
}

Vector operator - (const Vector& a, const Vector& b) {
	return { a.x - b.x, a.y - b.y };
}

数乘 operator * #

$$(x,y)\cdot k=(x\cdot k,y\cdot k)$$

Vector operator * (const Vector& p, var k) {
	return { p.x * k, p.y * k };
}

点乘 dot #

$$(x_1,y_1)\cdot(x_2,y_2)=x_1\cdot x_2 + y_1\cdot y_2$$

var dot(const Vector& a, const Vector& b) {
	return a.x * b.x + a.y * b.y;
}

叉乘 cross #

$$(x_1,y_1)\times(x_2,y_2)=x_1\cdot y_2 - x_2\cdot y_1$$

var cross(const Vector& a, const Vector& b) {
	return a.x * b.y - a.y * b.x;
}

取模 module #

$$\left\Vert(x, y)\right\Vert=\sqrt{x^2+y^2}$$

$$\left\Vert\vec{p}\right\Vert=\sqrt{\vec{p}\cdot\vec{p}}$$

var module(const Vector& p) {
	return sqrt(dot(p, p));
}

旋转 rotate #

向量 $\vec{p}$ 逆时针旋转 $\theta$ 弧度。

$$(x, y)\cdot \begin{pmatrix} \cos\theta & \sin\theta \\ -\sin\theta & \cos\theta \end{pmatrix}$$

Vector rotate(const Vector& p, var theta) {
	return {
		p.x * cos(theta) - p.y * sin(theta),
		p.x * sin(theta) + p.y * cos(theta)
	};
}

向量 vec #

从点 $A$ 指向点 $B$ 的向量。

Vector vec(const Point& a, const Point& b) {
	return b - a;
}

点|点 #

距离 to_point #

var to_point(const Point& a, const Point& b) {
	return module(a - b);
}

旋转 rotate #

点 $P$ 绕点 $C$ 逆时针旋转 $\theta$ 弧度。

Point rotate(const Point& p, const Point& c, var theta) {
	return c + rotate(p - c, theta);
}

点|线段 #

相交 on_seg #

点 $P$ 是否在线段 $AB$ 上。

bool on_seg(const Point& p, const Point& a, const Point& b) {
	return ! sign(cross(p - a, b - a)) and sign(dot(p - a, p - b)) <= 0;
}

距离 to_seg #

点 $P$ 到线段 $AB$ 的距离。

var to_seg(const Point& p, const Point& a, const Point& b) {
	if (a == b)
		return to_point(p, a);
	Vector x = p - a, y = p - b, z = b - a;
	if (sign(dot(x, z)) < 0)
		return module(x);
	if (sign(dot(y, z)) > 0)
		return module(y);
	return absolute(cross(x, z) / module(z));
}

点|直线 #

相交 on_line #

点 $P$ 是否在直线 $AB$ 上。

bool on_line(const Point& p, const Point& a, const Point& b) {
	return ! sign(cross(p - a, b - a));
}

距离 to_line #

点 $P$ 到直线 $AB$ 的距离。

var to_line(const Point& p, const Point& a, const Point& b) {
	Vector x = p - a, z = b - a;
	return absolute(cross(x, z) / module(z));
}

垂足 foot_point #

点 $P$ 到直线 $AB$ 的垂足。

Point foot_point(const Point& p, const Point& a, const Point& b) {
	Vector x = p - a, y = p - b, z = b - a;
	var prj1 = dot(x, z) / module(z);
	var prj2 = dot(y, z) / module(z);
	return a + z * (prj1 / (prj1 - prj2));
}

对称点 symmetry_point #

点 $P$ 关于直线 $AB$ 的对称点。

Point symmetry_point(const Point& p, const Point& a, const Point& b) {
	return foot_point(p, a, b) * 2 - p;
}

线|线 #

直线|直线 交点 cross_point #

直线 $AB$ 和直线 $CD$ 的交点。

Point cross_point(const Point& a, const Point& b, const Point& c, const Point& d) {
	Vector x = b - a, y = d - c, z = a - c;
	return a + x * (cross(y, z) / cross(x, y));
}

直线|线段 相交 inter_line_seg #

直线 $AB$ 和线段 $CD$ 是否相交。

bool inter_line_seg(const Point& a, const Point& b, const Point& c, const Point& d) {
	return on_seg(cross_point(a, b, c, d), c, d);
}

线段|线段 相交 inter_seg_seg #

线段 $AB$ 和线段 $CD$ 是否相交。

bool inter_seg_seg(const Point& a, const Point& b, const Point& c, const Point& d) {
	var c1 = cross(b - a, c - a), c2 = cross(b - a, d - a);
	var d1 = cross(d - c, a - c), d2 = cross(d - c, b - c);
	return sign(c1) * sign(c2) < 0 and sign(d1) * sign(d2) < 0;
}

多边形 #

有向面积 area #

逆时针为正向。

var area(const Poly& poly) {
	var s = 0;
	int n = poly.size();
	for (int i = 0; i < n; i ++)
		s += cross(poly[i], poly[(i + 1) % n]);
	return s / 2.0;
}

点|多边形 #

相交 on_poly #

点 $P$ 与多边形 $Poly$ 的关系。

  • 在外部:返回 $0$;
  • 在内部:返回 $1$;
  • 在边界:返回 $2$。
int on_poly(const Point& p, const Poly& poly) {
	int counter = 0;
	int n = poly.size();
	
	for (int i = 0; i < n; i ++) {
		const Point& a = poly[i];
		const Point& b = poly[(i + 1) % n];
		
		if (on_seg(p, a, b))
			return 2;
		
		if (p.y >= min(a.y, b.y) and p.y < max(a.y, b.y)) {
			var product = a.x + (p.y - a.y) / (b.y - a.y) * (b.x - a.x);
			counter += sign(product - p.x) > 0;
		}
	}
	
	return counter % 2;
}
int on_poly_binary(const Point& p, const Poly& poly) {
	
	static auto check = [&](const Point& a, const Point& l, const Point& r) {
		return sign(cross(l - a, r - a)) > 0;
	};
	
	int n = poly.size();
	
	if (check(poly[0], p, poly[1]) or check(poly[0], poly[n-1], p))
		return 0;
	if (on_seg(p, poly[0], poly[1]) or on_seg(p, poly[0], poly[n-1]))
		return 2;
	
	int l = -1, r = n;
	while (l + 1 < r) {
		int m = (l + r) / 2;
		if (check(poly[0], poly[m], p))
			l = m;
		else
			r = m;
	}
	
	if (check(poly[l], p, poly[l+1]))
		return 0;
	if (on_seg(p, poly[l], poly[l+1]))
		return 2;
	
	return 1;
}