这是一般用于检测凸多边形是否重叠的理论,并简称为SAT
首先 我们先看一下知乎上对于分离轴定理的定义:
我们定义分离轴是这样一个方向轴,两个convex物体分别投影到这个轴上,同时这两个物体的投影互不相交。因此,若给出两个convex物体A、B,若存在一条分离轴则这两个物体AB必不相交,否则这两个物体存在相交。
也就是说我们可以通过判断投影是否存在间隙来判断物体是否相碰撞。
第一部分,对于不规则图形,若是可以简化成外围为简单的几何凸多边形,我们就可以用此来判断。
下面记录我写的一道例题
给出两个矩形的四个位置坐标,试求是否碰撞,题保证若碰撞,两矩形的重叠面积至少占其中的一个矩形面积的10%
下面给出我copy加自己简化的代码(第一次写不会)
typedef struct {
float x,y;
}point;
point rt1[4],rt2[4];
float axis(point p,point o,point v_axis){
point p_o;
p_o.x=p.x-o.x;
p_o.y=p.y-o.y;
return p_o.x*v_axis.x+p_o.y*v_axis.y;
}
int check(point v_axis,point o){
float al=FLT_MAX,ar=-FLT_MAX,bl=FLT_MAX,br=-FLT_MAX;
for(int i=0;i<4;i++){
al=min(al,axis(rt1[i],o,v_axis));
ar=max(ar,axis(rt1[i],o,v_axis));
bl=min(bl,axis(rt2[i],o,v_axis));
br=max(br,axis(rt2[i],o,v_axis));
}
if(ar<=bl)return 0;
if(al>=br)return 0;
return 1;
}
int solve(){
point v_rt1_h,v_rt1_w,v_rt2_h,v_rt2_w;
v_rt1_h.x=rt1[3].x-rt1[0].x;
v_rt1_h.y=rt1[3].y-rt1[0].y;
v_rt1_w.x=rt1[1].x-rt1[0].x;
v_rt1_w.y=rt1[1].y-rt1[0].y;
v_rt2_h.x=rt2[3].x-rt2[0].x;
v_rt2_h.y=rt2[3].y-rt2[0].y;
v_rt2_w.x=rt2[1].x-rt2[0].x;
v_rt2_w.y=rt2[1].y-rt2[0].y;
int ans=1;
ans&=check(v_rt1_h,rt1[0]);
ans&=check(v_rt1_w,rt1[0]);
ans&=check(v_rt2_h,rt2[0]);
ans&=check(v_rt2_w,rt2[0]);
return ans;
}
首先我们可以从中得到一点经典的惯用技巧:
1,可以定义一个结构体来简化点的输入
2,可以定义多个结构体数组来描绘一个对象
3,需要包含<float.h>头文件来定义极大极小值
其次
我们开始分析函数
第一个是投影位置的长度
假设以o点为坐标原点,那么以o点链接的一条边为投影的x轴,得到v_axis,
输入一个坐标,那么其投影的长度公式为x1*x2+y1*y2;
可得到有向线段的长度
第二个是逐个坐标带入,求取对应矩形的投影长度最大最小值。
然后返回是否存在不相交的情况
若是遍历矩形的所有的边,仍未找到,返回1(碰撞)。
另外
我们一般处理空间上的点时,利用向量可以减少不必要的排序。
后记
只是学了这个定理,感觉其道理简朴,思绪明了,但纸上得来终觉浅,需要更深入的结合实践来巩固才好,同时此题也让我大开眼界,计算几何道阻且长。