一直以来感觉线段求交这种级别的算法很easy,甚至没有专门研究的必要,实际操作过程发现简约不简单,需要考虑各种特殊case。google一些后还是没有满意的结果,有理论的没有code,有code的没有理论甚至怀疑有漏洞。最后融会贯通后给出一个可用的版本:
1. 通过两个线段的包围盒快速拒绝。
2. 处理包围盒相交,但实际线段并不相交的case:
两条线段不相交,当且仅当:一条线段的两个端点全部都在另外一条线段的同一边。通过向量叉乘的符号判断,如下图:
P3、P4点在P1P2直线的同侧,当且仅当:P1P2向量与P2P3,P2P4的向量符号相同。
3. 判断线段相交的充分必要条件
两条线段相交,当且仅当下面每两对的叉乘结果有不同符号(或者每对中的一个为0)
// 叉乘顺时针为负,逆时针为正
S1 = (p1 - p4) x (p3 - p4) // p4p1 x p4p3 正 p1,p3,p4三角形面积两倍
S2 = (p2 - p4) x (p3 - p4) // p4p2 x p4p3 负 p2,p3,p4三角形面积两倍
S3 = (p3 - p2) x (p1 - p2) // p2p3 x p2p1 负 p1,p2,p3三角形面积两倍
S4 = (p4 - p2) x (p1 - p2) // p2p4 x p2p1 正 p1,p2,p4三角形面积两倍
根据步骤2中得出S1 * S2 > 0 || S3 * S4 > 0两个线段没有交点,剩余情况则有交点。 (2D中向量叉乘结果正好是一个数值)
4. 计算交点
1)S1==0 表示p1在p3,p4线段上面,p1为交点;2)S2==0 表示p2在p3,p4线段上面,p2为交点;
3)S3==0 表示p3在p1,p2线段上面,p3为交点;
4)S4==0 表示p4在p1,p2线段上面,p4为交点;
5)最后一种情况S1*S2 < 0 && S3*S4 < 0时,即最为常见的两个线段相互交叉情况,我们需要计算交点坐标。
利用上文已经预计算好叉乘,2D中两个向量叉乘的物理意义是面积,S1的一半等于p1,p3,p4三角形面积。设P1P2与P3P4交点为Q,则Q的坐标为P1、P2的加权平均,P1的权值为t1=P1Q / P1P2;P2的权值为t2=P2Q / P1P2,{ t1+t2 =1 and t1 > 0, t2 > 0 } 。
通过简单的三角形相似原理,可以得出:
t1 = |S1| / (| S1| + |S2|) => t1 = S1 / (S1 - S2)
t2 = |S2| / (| S1| + |S2|) => t2 = S2 / (S1 - S2)
最终Q为P1,P2加权平均:
Q.x = P1.x * t1 + P2.x * t2;
Q.y = p1.y * t1 + P2.y * t2
refer:
1. http://fins.iteye.com/blog/1522259
2. http://www.cppblog.com/wicbnu/archive/2009/08/24/94225.html