首先, 上个示意图.
根据图示, 线段a表示为端点a1和a2, 线段b表示为端点b1和b2. 为了利用向量的叉乘关系, 将线段的端点看成四个向量, 下面用粗体表示向量. 根据向量运算可知
a=a2-a1,
b=b2-b1.
将线段表示为参数方程:
a=a1 + t a
b=b1 + u b
其中参数t,u取值 [0,1]
两条线段相交具有如下关系:
a1 + t a=b1 + u b
将上式两边同时叉乘b, 得到:
(a1+t a) x b=(b1+u b) x b
由于b x b=0, 可得
a1 x b + t a x b=b1 x b
解出参数t
t=(b1-a1)x b/(a x b)
同理,解出参数u
u=a x (a1-b1)/(a x b)
当0<=t<=1,且0<=u<=1时,两线段有交点.
代入线段a的参数方程中, 即可得到线段交点坐标:
a1+t a
将上式中的中间变量用原始的线段端点表示, 即可得到根据线段端点表示的交点.
code 1
// Returns 1 if the lines intersect, otherwise 0. In addition, if the lines // intersect the intersection point may be stored in the floats i_x and i_y. char get_line_intersection(float p0_x, float p0_y, float p1_x, float p1_y, float p2_x, float p2_y, float p3_x, float p3_y, float *i_x, float *i_y) { float s1_x, s1_y, s2_x, s2_y; s1_x = p1_x - p0_x; s1_y = p1_y - p0_y; s2_x = p3_x - p2_x; s2_y = p3_y - p2_y; float s, t; s = (-s1_y * (p0_x - p2_x) + s1_x * (p0_y - p2_y)) / (-s2_x * s1_y + s1_x * s2_y); t = ( s2_x * (p0_y - p2_y) - s2_y * (p0_x - p2_x)) / (-s2_x * s1_y + s1_x * s2_y); if (s >= 0 && s <= 1 && t >= 0 && t <= 1) { // Collision detected if (i_x != NULL) *i_x = p0_x + (t * s1_x); if (i_y != NULL) *i_y = p0_y + (t * s1_y); return 1; } return 0; // No collision }
code 2
优化版本,排除线段平行情况,避免精度误差
int get_line_intersection(float p0_x, float p0_y, float p1_x, float p1_y, float p2_x, float p2_y, float p3_x, float p3_y, float *i_x, float *i_y) { float s02_x, s02_y, s10_x, s10_y, s32_x, s32_y, s_numer, t_numer, denom, t; s10_x = p1_x - p0_x; s10_y = p1_y - p0_y; s32_x = p3_x - p2_x; s32_y = p3_y - p2_y; denom = s10_x * s32_y - s32_x * s10_y; if (denom == 0)//平行或共线 return 0; // Collinear bool denomPositive = denom > 0; s02_x = p0_x - p2_x; s02_y = p0_y - p2_y; s_numer = s10_x * s02_y - s10_y * s02_x; if ((s_numer < 0) == denomPositive)//参数是大于等于0且小于等于1的,分子分母必须同号且分子小于等于分母 return 0; // No collision t_numer = s32_x * s02_y - s32_y * s02_x; if ((t_numer < 0) == denomPositive) return 0; // No collision if (fabs(s_numer) > fabs(denom) || fabs(t_numer) > fabs(denom)) return 0; // No collision // Collision detected t = t_numer / denom; if (i_x != NULL) *i_x = p0_x + (t * s10_x); if (i_y != NULL) *i_y = p0_y + (t * s10_y); return 1; }
方法来源于stack overflow的帖子:geometry - How do you detect where two line segments intersect?