【转载】判断两条线段是否相交——(向量叉乘)

原文:https://www.cnblogs.com/tuyang1129/p/9390376.html

实现:https://blog.csdn.net/yegshun/article/details/78273471

 

问题给出两条线段,问两线段是否相交?

 

向量叉乘(行列式计算)向量a(x1,y1),向量b(x2,y2):

首先我们要明白一个定理:向量a×向量b(×为向量叉乘),若结果小于0,表示向量b在向量a的顺时针方向;若结果大于0,表示向量b在向量a的逆时针方向;若等于0,表示向量a与向量b平行。(顺逆时针是指两向量平移至起点相连,从某个方向旋转到另一个向量小于180度)。如下图:

在上图中,OA×OB = 2 > 0, OB在OA的逆时针方向;OA×OC = -2 < 0,OC在OA的顺势针方向。即叉乘结果大于0,后一个在前一个的逆时针方向;小于零,后一个在前一个的顺时针方向。

 

那如何来判断两线段是否相交呢?

假设有两条线段AB,CD,若AB,CD相交,我们可以确定:

1.线段AB与CD所在的直线相交,即点A和点B分别在直线CD的两边;

2.线段CD与AB所在的直线相交,即点C和点D分别在直线AB的两边;

上面两个条件同时满足是两线段相交的充要条件,所以我们只需要证明点A和点B分别在直线CD的两边,点C和点D分别在直线AB的两边,这样便可以证明线段AB与CD相交了。

 

那判断两线段是否相交与一开始提到的向量叉乘定理有什么关系呢?有,我们可以通过叉乘来证明上面说的充要条件。看下图:

在上图中,线段AB与线段CD相交,于是我们可以得到两个向量AC,AD,C和D分别在AB的两边,向量AC在向量AB的逆势针方向,AB×AC > 0;向量AD在向量AB的顺势针方向,AB×AD < 0,两叉乘结果异号。

这样,方法就出来了:如果线段CD的两个端点C和D,与另一条线段的一个端点(A或B,只能是其中一个)连成的向量,与向量AB做叉乘,若结果异号,表示C和D分别在直线AB的两边,若结果同号,则表示CD两点都在AB的一边,则肯定不相交

当然,不能只证明C,D在直线AB的两边,还要用相同的方法证明A,B在直线CD的两边,两者同时满足才是线段相交的充要条件。

 

不过,线段相交还有一些特殊情况:

1.只有1点相交,如下图:

上图中,线段AB与CD相交于C点,按照之前介绍的方法,我们可以连成两向量AD和AC,这时候,我们发现,AC与AB共线,AB×AC = 0;而AB×AD < 0;两者并不异号,可实际上仍然相交。所以当出现两叉乘结果中,有一方为0,也可以看成点CD在直线AB的两边。

 

2.两条线段重合,如下图:

在上图中,线段AB与线段CD重合,重合部分为CB,这种重合的情况要特殊判断:

首先,我们给没条线段的两个端点排序,大小判断方法如下:横坐标大的点更大,横坐标相同,纵坐标大的点更大。

排好序后,每条线段中,小的点当起点,大的当终点。我们计算向量AB×向量CD,若结果为0,表示线段AB平行CD,平行才有了重合的可能;但平行也分共线和不共线,只有共线才有可能重合,看下图:

上图中,第一种情况不共线,第二种情况共线。那如何来判断是否共线呢?

我们可以在两条线段中各取一点,用这两点组成的向量与其中一条线段进行叉乘,结果若为0,就表示两线段共线,如下图:

我们取向量BC,若BC×CD = 0,表示两点共线,即是第二种情况,否则就是第一种情况。第一种情况肯定不相交。猴子为什么不喜欢平行线?因为他们没有相交。。。(尬)

然然然然然而,即使他们共线,却还是不一定重合,就如上图中第二种情况。这时候,之前给点排序的妙处就体现出来了:

若一条线段AB与另一条线段CD共线,且线段AB的起点小于等于线段CD的起点,但线段AB的终点(注意是终点)大于等于线段CD的起点(注意是起点),或者交换一下顺序,CD的起点小于AB的起点......只要满足其中一个,就表示有重合部分

 

最上面的实现的链接中,算法3的原理(http://dec3.jlu.edu.cn/webcourse/t000096/graphics/chapter5/01_1.html):

但是根据实际测试有问题,

两个测试用例:

一、A=(1,1),B=(4,2),C=(2,3),D=(2,4)<==>delta=3,namenda=0.6,miu=0.3

而实际上AB与CD是不相交的

二、A=(1,1),B=(4,2),C=(2,3),D=(3,4)<==>delta=2,namenda=0.5,miu=0.5

一样不相交

先放弃这个算法。

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
判断两条线段是否相交的一种常用的方法是利用向量叉积。具体步骤如下: 1. 将线段 AB 表示为向量 AB,将线段 CD 表示为向量 CD。 2. 如果向量 AB 和向量 CD 的叉积为零,则表示两条线段共线,需要进一步判断它们是否有重叠部分。 3. 如果向量 AB 和向量 CD 的叉积不为零,则表示两条线段不共线。此时需要判断向量 AC 和向量 AD 是否向量 AB 的两侧,以及向量 CA 和向量 CB 是否向量 CD 的两侧。如果满足这个条件,则表示两条线段相交。 下面是 Java 代码实现: ```java public class LineIntersect { // 判断两个向量是否在同一侧 private static boolean sameSide(Vector2D v1, Vector2D v2, Vector2D p1, Vector2D p2) { double cross1 = v1.crossProduct(p1); double cross2 = v1.crossProduct(p2); return (cross1 * cross2 >= 0); } // 判断两条线段是否相交 public static boolean intersect(Line2D line1, Line2D line2) { Vector2D p1 = new Vector2D(line1.getP1()); Vector2D p2 = new Vector2D(line1.getP2()); Vector2D p3 = new Vector2D(line2.getP1()); Vector2D p4 = new Vector2D(line2.getP2()); Vector2D v1 = p2.subtract(p1); Vector2D v2 = p3.subtract(p1); Vector2D v3 = p4.subtract(p1); if (Math.abs(v1.crossProduct(v2)) < 1e-6 || Math.abs(v1.crossProduct(v3)) < 1e-6) { // 两条线段共线,需要判断它们是否有重叠部分 if (line1.intersectsLine(line2)) { return true; } else { return false; } } else if (sameSide(v1, v2, v1, v3) && sameSide(v1, v3, v1, v2) && sameSide(v3, v4, v3, v1) && sameSide(v3, v1, v3, v4)) { // 两条线段不共线,并且向量 AC 和向量 AD 在向量 AB 的两侧,向量 CA 和向量 CB 在向量 CD 的两侧 return true; } else { return false; } } } ``` 其中 Vector2D 是一个二维向量类,包含向量的加、减、点乘、叉乘等基本运算。Line2D 是 Java 自带的表示线段的类。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值