计算几何
基础
向量叉乘
- A (x1,y1) B(x2,y2) A×B= x1y2-x2y1
- 叉乘判断向量位置,
- P×Q < 0 P在 Q 左侧,
- P×Q > 0 P在 Q 右侧,
- P×Q > 0 P与 Q 共线,
向量点乘
- A (x1,y1) B(x2,y2) A·B=x1x2+y1y2 a*b=|a||b|cos0
凸包
Jarvis步进法:
O(nH)
- (点少时,H是点个数) 建议 用Graham法
思路:
- 纵坐标最小的那个点一定是凸包上的点,例如图上的 P0。
- 从 P0 开始,按逆时针的方向,逐个找凸包上的点,每前进一步找到一个点,所以叫作步进法。
- 怎么找下一个点呢?利用夹角。假设现在已经找到 {P0,P1,P2} 了,要找下一个点:剩下的点分别和 P2 组成向量,设这个向量与向量P1P2的夹角为 β 。当 β 最小时就是所要求的下一个点了,此处为 P3 。
注意:
- 找第二个点 P1 时,因为已经找到的只有 P0 一个点,所以向量只能和水平线作夹角 α,当 α 最小时求得第二个点。
共线情况:如果直线 P2P3 上还有一个点 P4,即三个点共线,此时由向量P2P3 和向量P2P4 产生的两个 β 是相同的。我们应该把 P3、P4 都当做凸包上的点,并且把距离 P2 最远的那个点(即图中的P4)作为最后搜索到的点,继续找它的下一个连接点。
Graham法
:
O(n㏒n)
时间复杂度:O(n㏒n)
思路:Graham扫描的思想和Jarris步进法类似,也是先找到凸包上的一个点,然后从那个点开始按逆时针方向逐个找凸包上的点,但它不是利用夹角。
步骤:
把所有点放在二维坐标系中,则纵坐标最小的点一定是凸包上的点,如图中的P0。
把所有点的坐标平移一下,使 P0 作为原点,如上图。
计算各个点相对于 P0 的幅角 α ,按从小到大的顺序对各个点排序。当 α 相同时,距离 P0 比较近的排在前面。例如上图得到的结果为 P1,P2,P3,P4,P5,P6,P7,P8。我们由几何知识可以知道,结果中第一个点 P1 和最后一个点 P8 一定是凸包上的点。
(以上是准备步骤,以下开始求凸包)
以上,我们已经知道了凸包上的第一个点 P0 和第二个点 P1,我们把它们放在栈里面。现在从步骤3求得的那个结果里,把 P1 后面的那个点拿出来做
当前点
,即 P2 。接下来开始找第三个点:
连接P0和栈顶的那个点,得到直线 L 。看
当前点
是在直线 L 的右边还是左边。如果在直线的右边就执行步骤5;如果在直线上,或者在直线的左边就执行步骤6。
如果在右边,则栈顶的那个元素不是凸包上的点,把栈顶元素出栈。执行步骤4。
当前点是凸包上的点,把它压入栈,执行步骤7。
检查当前的点 P2 是不是步骤3那个结果的最后一个元素。是最后一个元素的话就结束。如果不是的话就把 P2 后面那个点做当前点,返回步骤4。
最后,栈中的元素就是凸包上的点了。
以下为用Graham扫描法动态求解的过程:
应用
规则图形
判断点在线段上
点
Q(x,y),
线段
P1P2 (x2-x1,y2-y1)
则向量共线
P1Q
×
P1P2 =0
即(x(y2-y1)-(x2-x1)y)= 0
并且 min(x1,x2) <=x<=max(x1,x2)&&min(y1,y2)<=y<=max(y1,y2)// 在线段内部
判断两线段相交
快速排斥实验
- 两个线段为对角线组成的矩形R和矩形T 若R,T不想交-->不想交,若相交进行跨立实验判断
- 方法: 假设 P1 = (x1, y1), P2 = (x2, y2), Q1 = (x3, y3), Q2 = (x4, y4),设矩形 R 的 x 坐标的最小边界为 RX1 = min(x1, x2),RX2=max(x1,x2) ,RY1=min(y1,y2)以此类推,将矩形表示为 R = (RX1, RY1, RX2, RY2) 的形式,若两矩形相交,则相交的部分构成了一个新的矩形 F,我们可以知道 F 的 FX1 = max(RX1, TX1), FY2 = max(RY1, TY1),
- FX2 = min(RX2, TX2), FY2 = min(RY2, TX2),得到 F 的各个值之后,只要判断矩形 F 是否成立就知道 R 和 T 到底有没有相交了,若 FX1 > FX2 或 FY1 > Fy1 则 F 无法构成,RT不相交,否则 RT相交
跨立实验(互相跨立)
- Q1P1×Q1P2 * Q1Q2×Q1Q1P2 >=0 && Q1P2×P1P2 * P1P2×Q2P2 >=0
线段与圆相交
三种情况, 第一种, 两个端点都在圆内,不相交,第二种,一个在圆外,一个圆内 一定相交;
第三种都在圆外,需要判断;
double
x,y,r;
vector
<
pair
<
double
,
double
>
>
V;
typedef
pair
<
double
,
double
>
PAIR;
PAIR yuan;
bool
judge(PAIR P)
// 判断是否在圆内
{
if
( (P.first
-
x)
*
(P.first
-
x)
+
(P.second
-
y)
*
(P.second
-
y)
-
r
*
r
<=0
)
return
1
;
return
0
;
}
bool
Judis(PAIR P1,PAIR P2,
double
R)
//线段与圆的关系
{
if
(judge(P1)
&&
judge(P2))
//都在圆内 不相交
return
false
;
if
(
!
judge(P1)
&&
judge(P2)
||
judge(P1)
&&!
judge(P2))
//一个圆内一个圆外 相交
return
true
;
double
A,B,C,dist1,dist2,angle1,angle2;
//Ax+By+C=0;//(y1-y2)x +(x2-x1)y +x1y2-y1x2=0
if
(P1.first
==
P2.first)
A
=1
,B
=0
,C
=
-
P1.first;
else
if
(P1.second
==
P2.second)
A
=0
,B
=1
,C
=
-
P1.second;
else
{
A
=
P1.second
-
P2.second;
B
=
P2.first
-
P1.first;
C
=
P1.first
*
P2.second
-
P1.second
*
P2.first;
}
dist1
=
A
*
yuan.first
+
B
*
yuan.second
+
C;
dist1
*=
dist1;
dist2
=
(A
*
A
+
B
*
B)
*
R
*
R;
if
(dist1
>
dist2)
return
false
;
//点到直线距离大于半径r 不相交
angle1
=
(yuan.first
-
P1.first)
*
(P2.first
-
P1.first)
+
(yuan.second
-
P1.second)
*
(P2.second
-
P1.second);
angle2
=
(yuan.first
-
P2.first)
*
(P1.first
-
P2.first)
+
(yuan.second
-
P2.second)
*
(P1.second
-
P2.second);
if
(angle1
>
0
&&
angle2
>
0
)
return
true
;
//余弦都为正,则是锐角 相交
return
false
;
//不相交
}
判断线段和直线相交
- 化成线段相交
判断点在矩形内
- 点在矩形对角线坐标内
判断线段/折现/多边形在矩形内
- 转换为所有点在矩形内
判断矩形在矩形内
- 上下界问题
判断圆在矩形内
- 圆心在矩形内
- 圆的半径R小于min(圆心到四个边距离d)
不规则图形
判断点在多边形
- 多边形问题,射线法
代码:
https://paste.ubuntu.com/26455451/
#include
<stdio.h>
#include
<bits/stdc++.h>
#include
<iostream>
using
namespace
std;
const
double
ESP
=1e-6
;
const
double
INFINV
=1e10
;
struct
LineSegment{
struct
Point{
double
x,y;
};
Point Pt1,Pt2;
typedef
vector
<
Point
>
Vector;
// 叉乘 |p0p1| x |p0p2|
double
Multiply(Point p1,Point p2,Point p0)
{
return
( (p1.x
-
p0.x)
*
(p2.y
-
p0.y)
-
(p2.x
-
p0.x)
*
(p1.y
-
p0.y));
}
// 点是否在边上
bool
IsOnline (Point P,LineSegment line)
{
return
( ( fabs( Multiply(line.Pt1,line.Pt2,P) ) )
<
ESP
&&
( (P.x
-
line.Pt1.x)
*
(P.x
-
line.Pt2.x)
<=0
)
&&
( (P.y
-
line.Pt1.y)
*
(P.y
-
line.Pt2.y)
<=0
) ) ;
}
bool
Intersect(LineSegment L1,LineSegment L2)
{
return
( (max(L1.Pt1.x, L1.Pt2.x)
>=
min(L2.Pt1.x, L2.Pt2.x))
&&
(max(L2.Pt1.x, L2.Pt2.x)
>=
min(L1.Pt1.x, L1.Pt2.x))
&&
(max(L1.Pt1.y, L1.Pt2.y)
>=
min(L2.Pt1.y, L2.Pt2.y))
&&
(max(L2.Pt1.y, L2.Pt2.y)
>=
min(L1.Pt1.y, L1.Pt2.y))
&&
(Multiply(L2.Pt1, L1.Pt2, L1.Pt1)
*
Multiply(L1.Pt2, L2.Pt2, L1.Pt1)
>=
0
)
&&
(Multiply(L1.Pt1, L2.Pt2, L2.Pt1)
*
Multiply(L2.Pt2, L1.Pt2, L2.Pt1)
>=
0
) );
}
bool
main_Judge_Polygon(
const
Vector
&
poly,Point P)
{
int
n
=
poly.size();
int
count
=0
;
LineSegment line;
line.Pt1
=
P;
line.Pt2.y
=
P.y;
line.Pt2.x
=
-
INFINV;
for
(
int
i
=0
;i
<
n;i
++
)
{
LineSegment side;
side.Pt1
=
poly[i];
side.Pt2
=
poly[ (i
+1
)
%
n];
if
( IsOnline(P,side) )
{
// 若点在边上 直接返回
return
true
;
}
if
( fabs(side.Pt1.y
-
side.Pt2.y)
<
ESP )
{
//判断side是否平行于X轴
continue
;
}
if
( IsOnline(side.Pt1, line) )
{
if
( side.Pt1.y
>
side.Pt2.y )
count
++
;
}
else
if
( IsOnline(side.Pt2,line) )
{
if
( side.Pt2.y
>
side.Pt2.y)
count
++
;
}
else
if
( Intersect (line ,side))
{
count
++
;
}
}
if
(count
%2==1
)
return
false
;
return
true
;
}
}JK;
int
main()
{
return
0
;
}