写在前面
本文基于fxj巨佬的计算几何全家桶,并基于原文进行了自己的一些整理了经验补充,阅读本文前请前往支持巨佬fxj。
表示:
用一个
V
V
V数组顺时针或者逆时针存储多边形的所有顶点。
调用函数时传入数组和顶点数。
求多边形面积:
取任意一个点
O
O
O,多边形面积为:
S
=
1
2
∑
i
=
1
n
O
A
i
→
×
O
A
→
i
%
n
+
1
S=\frac{1}{2}\sum^{n}_{i=1} \overrightarrow{OA_i} \times\overrightarrow{OA}_{i\%n+1}
S=21i=1∑nOAi×OAi%n+1
inline double S(const V *a,const int n)
{
double res(0);
for(int i=1;i<=n;++i) res+=(a[i]^a[i%n+1]);
return res/2;
}
判断多边形的凹凸性
取相邻的三个点
(
a
,
b
,
c
)
( a , b , c )
(a,b,c)作为一个三元组。
判断多边形凹凸的充要条件:对于多边形所有的
n
n
n个这样三元组,
c
c
c和
a
b
ab
ab的位置关系(指顺时针或逆时针)必然全部都是相同的。
叉积判断符号即可。
注意如果没有保证相邻三个点不共线的话需要特判。
时间复杂度
O
(
n
)
O ( n )
O(n).
bool is_convex(const V *a,const int n)
{
a[0]=a[n],a[n+1]=a[1];
int op=0;
for(int i=1;i<=n;++i)
{
double o=(a[i+1]-a[i])^(a[i]-a[i-1]);
if(abs(o)<eps) continue;
int nop=o>0?1:-1;
if(!op) op=nop;
else if(op!=nop) return false;
}
return true;
}
判定点在多边形内
一个看起来很好写但写起来很恶心的东西。
推荐使用点数判别法。
向左水平引出一条射线,计算其与多边形产生多少个交点,奇内偶外,然后还有亿点点细节:
- 如果当前边经过了该点,直接返回相应结果
- 由于可能出现射线经过多边形顶点导致一个点被计算两次的情况,强制令这个点在下面的边被统计。实现上,当 m a x ( y 1 , y 2 ) = y 0 max(y_1,y_2)=y_0 max(y1,y2)=y0时,统计该次答案, m i n ( y 1 , y 2 ) = y 0 min(y_1,y_2)=y_0 min(y1,y2)=y0而当时,不统计该次答案。
- 出现水平边时,直接忽略即可,结合第二条的处理原则,就依然是正确的.
int in_poly(const V *a,const int n,const V o){
int res(0);
for(int i=1;i<=n;i++){
V u=a[i],v=a[i+1];
if(on_seg(trans(u,v),o)) return 1;//on the edge
if(abs(u.y-v.y)<eps) continue;//ignore horizontal
if(max(u.y,v.y)<o.y-eps) continue;//equal will not continue
if(min(u.y,v.y)>o.y-eps) continue;//equal will continue
double x=u.x+(o.y-u.y)/(v.y-u.y)*(v.x-u.x);
if(x<o.x) res^=1;
}
return res?2:0;//odd:in even:out
}
关于判断射线和多边形的某一条边是否有交点的正确性。
函数中给出的算法就是求点
K
K
K的坐标,通过比较
X
K
X_K
XK和
X
O
X_O
XO即可判断从
O
O
O水平向左的射线是否与线段
U
V
UV
UV有交点。