求两个线段交点

一.判断线段相交(跨立实验)

  • 叉乘

        若\vec{a}表示为(x1,y1)\vec{b}表示为(x2,y2)则:

        \vec{a}\times \vec{b}=(x1*y2-x2*y1,x2*y1-x1*y2)

        叉积的模:

        |\vec{a}\times \vec{b}|=|x1*y2-x2*y1|

我们要用的只是由x1∗y2−x2∗y1的正负判断两个向量的位置关系(正表示a由逆时针旋转更容易得到b)

首先我们要明白一个定理:向量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的起点......只要满足其中一个,就表示有重合部分。

二.求交点

可以通过线段的跨立实验判断线段是否相交,但是想要进一步求它们的交点还是比较麻烦。

 完整推导::https://www.cnblogs.com/huntto/p/17492406.html

直接贴过来:

        

 下面的代码就是用这个公式实现的

三.求线段交点的C++ 代码

  /** A general line-line intersection test
     @param A   the startpoint for the first line L1 = (A - B)
     @param B   the endpoint for the first line L1 = (A - B)
     @param C   the startpoint for the second line L2 = (C - D)
     @param D   the endpoint for the second line L2 = (C - D)
     @param S   the range for a hitpoint in L1 (p = A + S*(B - A))
     @param T   the range for a hitpoint in L2 (p = C + T*(D - C))
     @return    whether these two lines intersects.

     Note that to truly test intersection for segments we have to make
     sure that S & T lie within [0..1] and for rays, make sure S & T > 0
     the hit point is        C + T * (D - C);
     the hit point also is   A + S * (B - A);
     @since 3.0
     * @js NA
     * @lua NA
     */
    static bool isLineIntersect(const Vec2& A, const Vec2& B,
                                 const Vec2& C, const Vec2& D,
                                 float *S = nullptr, float *T = nullptr);


bool Vec2::isLineIntersect(const Vec2& A, const Vec2& B,
                            const Vec2& C, const Vec2& D,
                            float *S, float *T)
{
    // FAIL: Line undefined
    if ( (A.x==B.x && A.y==B.y) || (C.x==D.x && C.y==D.y) )
    {
        return false;
    }
    
    const float denom = crossProduct2Vector(A, B, C, D);
    
    if (denom == 0)
    {
        // Lines parallel or overlap
        return false;
    }
    
    if (S != nullptr) *S = crossProduct2Vector(C, D, C, A) / denom;
    if (T != nullptr) *T = crossProduct2Vector(A, B, C, A) / denom;
    
    return true;
}

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
两个线段交点,可以使用以下步骤来实现: 1. 首先,定义一个表示线段的结构体或类。可以使用两个点来表示线段的起点和终点。 ```csharp public struct LineSegment { public Point StartPoint { get; set; } public Point EndPoint { get; set; } } ``` 2. 编写一个函数来计算两个线段交点。可以使用线段的参数方程来解。 ```csharp public static Point? GetIntersectionPoint(LineSegment line1, LineSegment line2) { // 获取线段1的参数 double x1 = line1.StartPoint.X; double y1 = line1.StartPoint.Y; double x2 = line1.EndPoint.X; double y2 = line1.EndPoint.Y; // 获取线段2的参数 double x3 = line2.StartPoint.X; double y3 = line2.StartPoint.Y; double x4 = line2.EndPoint.X; double y4 = line2.EndPoint.Y; // 计算分母 double denominator = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4); // 当分母为0时,表示两线段平行或重合,没有交点 if (denominator == 0) return null; // 计算交点的坐标 double x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / denominator; double y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / denominator; // 检查交点是否在两个线段上 if (x < Math.Min(x1, x2) || x > Math.Max(x1, x2) || x < Math.Min(x3, x4) || x > Math.Max(x3, x4) || y < Math.Min(y1, y2) || y > Math.Max(y1, y2) || y < Math.Min(y3, y4) || y > Math.Max(y3, y4)) return null; return new Point(x, y); } ``` 3. 创建两个线段对象,然后调用函数获取交点。 ```csharp LineSegment line1 = new LineSegment { StartPoint = new Point(0, 0), EndPoint = new Point(10, 10) }; LineSegment line2 = new LineSegment { StartPoint = new Point(0, 10), EndPoint = new Point(10, 0) }; Point? intersectionPoint = GetIntersectionPoint(line1, line2); if (intersectionPoint.HasValue) { Console.WriteLine($"Intersection point: ({intersectionPoint.Value.X}, {intersectionPoint.Value.Y})"); } else { Console.WriteLine("No intersection point."); } ``` 这样,你就可以两个线段交点了。注意,如果两个线段平行或重合,将会返回 null。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值