计算几何

基础部分


高精度圆周率

const double pi = acos(-1.0);

偏差值

const double eps = 1e-8;

sgn

int sgn(double x)  // 判断x是否等于0
{ 
	if (fabs(x) < eps)
		return 0;
	else
		return x < 0 ? -1 : 1;
}

dcmp

int dcmp(double x, double y) // 比较两个浮点数,0为相等,-1为小于,1为大于
{ 
	if (fabs(x - y) < eps)
		return 0;
	else
		return x < y ? -1 : 1;
}

点和向量


struct Point 
{
	double x, y;
	Point() {}
	Point(double x, double y) : x(x), y(y) {}
};

两点间距离

double Dist(Point A, Point B)
 {
1: return hypot(A.x - B.x, A.y - B.y);
2: return sqrt((A.x - B.x) * (A.x - B.x) + (A.y - B.y) * (A.y - B.y));
}

向量

typedef Point Vector;

向量的运算

struct Point 
{
	double x, y;
	Point() {}
	Point(double x, double y) : x(x), y(y) {}
	Point operator+(Point B) { return Point(x + B.x, y + B.y); }
// 加
	Point operator-(Point B) { return Point(x - B.x, y - B.y); }
// 减
	Point operator*(double k) { return Point(x * k, y * k); }
// 乘
	Point operator/(double k) { return Point(x / k, y / k); }
// 除
	bool operator==(Point B) { return sgn(x - B.x) == 0 && sgn(y - B.y) == 0; }
// 等于
};

点积


定义

A·B=|A| |B| cosθ

几何意义

θ:表示向量β与向量α的夹角
向量β在向量α上的投影长度乘以向量α的模长

公式

A · B = A.x * B.x + A.y * B.y

代码

double Dot(Vector A, Vector B) 
{
	return A.x * B.x + A.y * B.y;
}

应用

1. 判断向量A与向量B的夹角是钝角还是锐角

若 Dot(A,B) > 0,说明向量A与向量B的夹角是锐角
若 Dot(A,B) < 0,说明向量A与向量B的夹角是钝角
若 Dot(A,B) = 0,说明向量A与向量B的夹角是直角

2. 求向量A的模长

double Len(Vector A) 
{
	return sqrt(Dot(A, A));
}

求向量 模长的平方

double Len2(Vector A) 
{
	return Dot(A, A);
}

3. 求向量 与向量 的夹角大小

double Angle(Vector A, Vector B) 
{
	return acos(Dot(A, B) / Len(A) / Len(B));
}

叉积


定义

A×B=|A| |B| sinθ

几何意义

θ:向量α旋转到向量β所经过的夹角
|A×B| 在数值上等于由向量α和向量β构成的平行四边形的面积

代码

double Cross(Vector A, Vector B) 
{
	return A.x * B.y - A.y * B.x;
}

应用

1. 判断向量α与向量β的方向关系

若 A × B > 0, B 在 A 的逆时针方向
若 A × B < 0, B 在 A 的顺时针方向
若 A × B = 0,B 与 A 共线,可能是同方向,也可能是反方向

2… 计算两向量构成的平行四边形的有向面积

3个点 A, B, C,A 以 为公共点,得到两个向量 B - A,C - A ,它们构成的平行四边形的面积

如下

double Area2(Point A, Point B, Point C) 
{
	return Cross(B - A, C - A);
}

三角形面积则就是Area2( A, B, C) / 2

3. 向量旋转

向量A逆时针旋转的角度为 rad

Vector Rotate(Vector A, double rad) 
{
	return Vector(A.x * cos(rad) - A.y * sin(rad), A.x * sin(rad) + A.y *
cos(rad));
}

有时需要求单位法向量,即逆时针旋转90° ,然后取单位值

Vector Normal(Vector A) 
{
	return Vector(-A.y / Len(A), A.x / Len(A));
}

4. 用叉积检查两个向量是否平行或重合

bool Parallel(Vector A, Vector B) 
{
	return sgn(Cross(A, B)) == 0;
}

点和线


代码

struct Line 
{ // 直线
	Point p1, p2; // 线上的两个点
	Line() {}
		// 直接用两个点来构造直线
	Line(Point p1, Point p2) : p1(p1), p2(p2) {}
		// 根据一个点和倾斜角angle确定直线,0≤angle≤pi
	Line(Point p, double angle) 
	{
		p1 = p;
		if (sgn(angle - pi / 2) == 0)
			p2 = (p1 + Point(0, 1));
		else
			p2 = (p1 + Point(1, tan(angle)));
	}
		// ax + by + c = 0
	Line(double a, double b, double c) 
	{
		if (sgn(a) == 0) 
		{
			p1 = Point(0, -c / b);
			p2 = Point(1, -c / b);
		} 
		else if (sgn(b) == 0) 
		{
			p1 = Point(-c / a, 0);
			p2 = Point(-c / a, 1);
		} 
		else 
		{
			p1 = Point(0, -c / b);
			p2 = Point(1, (-c - a) / b);
		}
	}
};

线段的表示

typedef Line Segment;

点和直线的位置关系

用直线v上的两点 p1 和 p2 与点p 构成两个向量,用叉积的正负判断方向,得到相对的位置关系

int Point_line_relation(Point p, Line v) 
{
	int c = sgn(Cross(p - v.p1, v.p2 - v.p1));
	if (c < 0) // 1:p在v的左边
		return 1;
	if (c > 0) // 2:p在v的右边
		return 2;
	return 0; // 0:p在v上
}

点和线段的位置关系

判断点p 是否在线段v 上,先用叉积判断是否共线,然后用点积看 p 和 v 的两个端点产生的角是否为钝角

bool Point_on_seg(Point p, Line v) 
{ // 0为点不在线段v上;1为点在线段v上
	return sgn(Cross(p - v.p1, v.p2 - v.p1)) == 0 && sgn(Dot(p - v.p1, p - v.p2)) <= 0;
}

点到直线的距离

已知点p 和直线v(p1, p2) ,求 p 到 v 的距离。
首先用叉积求 p,p1 ,p2 构成的平行四边形的面积,然后用
面积除以平行四边形的底边长,也就是线段 (p1, p2) 的长度,就得到了平行四边形的高,即点 p 到直线 v 的距离

double Dis_point_line(Point p, Line v) 
{
	return fabs(Cross(p - v.p1, v.p2 - v.p1)) / Dist(v.p1, v.p2);
}

点在直线上的投影

Point Point_line_proj(Point p, Line v) 
{ // 点p在直线v上的投影
	double k = Dot(v.p2 - v.p1, p - v.p1) / Len2(v.p2 - v.p1);
	return v.p1 + (v.p2 - v.p1) * k;
}

点关于直线的对称点

Point Point_line_symmetry(Point p, Line v) 
{ // 点p关于直线v的对称点
	Point q = Point_line_proj(p, v);
	return Point(2 * q.x - p.x, 2 * q.y - p.y);
}

点到线段的距离

对于点 p 到线段v(p1, p2) 的距离,在一下 3 个距离中取最小值:从 p出发对线段 v 做垂线,如果交点在 v 上,这个距离就是最小值;p 到 p1 的距离,p 到 p2 的距离

double Dis_point_seg(Point p, Segment v) 
{
	if (sgn(Dot(p - v.p1, v.p2 - v.p1)) < 0 || sgn(Dot(p - v.p2, v.p1 - v.p2)) < 0)
		return min(Dist(p, v.p1), Dist(p, v.p2));
	return Dis_point_line(p, v);
}

两条直线的位置关系

int Line_relation(Line v1, Line v2) 
{
	if (sgn(Cross(v1.p2 - v1.p1, v2.p2 - v2.p1)) == 0) 
	{
		if (Point_line_relation(v1.p1, v2) == 0)
			return 1; // 1:重合
		else
			return 0; // 0:平行
	}
	return 2; // 2:相交
}

求两条直线的交点

Point Cross_point(Point a, Point b, Point c, Point d) 
{ // Line: ab, Line: cd
	double s1 = Cross(b - a, c - a);
	double s2 = Cross(b - a, d - a);
	return Point(c.x * s2 - d.x * s1, c.y * s2 - d.y * s1) / (s2 - s1);
}

判断两个线段是否规范相交

这里利用叉积有正负的特点。如果一条线段的两端在另一条线段的两侧,那么两个端点与另一线段产生的两个叉积的正负相反,也就是说两个叉积相乘为负。如果两条线段互相满足这一点,那么就是规范相交的。

规范相交:交点在线段内部
非规范相交:交点在某条线段的端点

bool Cross_segment(Point a, Point b, Point c, Point d) 
{
// 规范相交
	double c1 = Cross(b - a, c - a), c2 = Cross(b - a, d - a);
	double d1 = Cross(d - c, a - c), d2 = Cross(d - c, b - c);
	return sgn(c1) * sgn(c2) < 0 && sgn(d1) * sgn(d2) < 0; 
	// 1: 相交,0: 不相交
// 非规范相交
	return max(a.x, b.x) >= min(c.x, d.x) && max(c.x, d.x) >= min(a.x, b.x) && max(a.y, b.y) >= min(c.y, d.y) && max(c.y, d.y) >= min(a.y, b.y) && sgn(Cross(b - a, c - a)) * sgn(Cross(b - a, d - a)) <= 0 && sgn(Cross(d - c, a - c)) * sgn(Cross(d - c, b - c)) <= 0;
}

多边形


## 代码
Point p[N];

应用

1. 判断点是否在多边形内部

int Point_in_polygon(Point pt, Point* p, int n) 
{ // 点pt,多边形Point* p
	for (int i = 0; i < n; ++i)
		if (p[i] == pt)
			return 3; // 3: 点在多边形的顶点上
	for (int i = 0; i < n; ++i) 
	{
		Line v = Line(p[i], p[(i + 1) % n]);
		if (Point_on_seg(pt, v))
			return 2; // 2: 点在多边形的边上
	}
	int num = 0;
	for (int i = 0; i < n; ++i) 
	{
		int j = (i + 1) % n;
		int c = sgn(Cross(pt - p[j], p[i] - p[j]));
		int u = sgn(p[i].y - pt.y);
		int v = sgn(p[j].y - pt.y);
		if (c > 0 && u < 0 && v >= 0)
			++num;
		if (c < 0 && u >= 0 && v < 0)
			--num;
	}
	return num != 0; // 1: 点在内部;0: 点在外部
}

2. 多边形的面积

double Polygon_area(Point* p, int n) 
{
	double area = 0;
	for (int i = 0; i < n; ++i)
		area += Cross(p[i], p[(i + 1) % n]);
	return area / 2;
}

3. 求多边形的重心

Point Polygon_center(Point* p, int n) 
{
	Point ans(0, 0);
	if (Polygon_area(p, n) == 0)
		return ans;
	for (int i = 0; i < n; ++i)
		ans = ans + (p[i] + p[(i + 1) % n]) * Cross(p[i], p[(i + 1) % n]);
	return ans / Polygon_area(p, n) / 6;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值