平面中直线与直线相交交点的两种求解方法

648 阅读4分钟

1.前言

前面,通过文章详解向量与Unity中向量类Vcetor3的使用(一)我们学习向量及向量的基本运算,在文章详解向量与Unity中向量类Vcetor3的使用(二)中对Unity中向量类Vector3结合向量原理进行了详细的了解与其实现。

本篇文章,我们结合向量了解下Unity中直线与直线相交的交点坐标求解,结合自己使用,介绍下面两种便捷且容易理解的方法:方程组求解与向量法。

2.直线与直线相交-方程组求解

2.1求解过程

已知条件:直线L1L_1上的两点P1=(x1,y1)P_1=(x_1,y_1),P2=(x2,y2)P_2=(x_2,y_2),直线L2L_2上的两点P3=(x3,y3)P_3=(x_3,y_3),P4=(x4,y4)P_4=(x_4,y_4),求两条直线的交点II

我们熟知的直线方程式的表达式为斜截式y=mx+by=mx+b,一般式为Ax+By+C=0Ax+By+C=0(A、B、C不同时为零),知道两点坐标可求斜率:

m=y2y1x2x1m=\frac {y_2-y_1}{x_2-x_1}

再将点P1=(x1,y1)P_1=(x_1,y_1)代入式子中,可得b=y1mx1b=y_1-mx_1我们可得到直线L1L_1的斜截式表达式:

y=y2y1x2x1x+(y1mx1)y=\frac {y_2-y_1}{x_2-x_1}x+(y_1-mx_1)

我们将其转化为一般式方程式:

(y2y1)x+(x1x2)y=(x1y2x2y1)(y_2-y_1)x+(x_1-x_2)y=(x_1y_2-x_2y_1)

简洁表示为:

a1x+b1y=c1a_1x+b_1y=c_1

其中:

a1=y2y1b1=x1x2c1=x1y2x2y1a_1=y_2-y_1,b_1=x_1-x_2,c_1=x_1y_2-x_2y_1

同理可得直线L2L_2的一般式方程式如下:

(y4y3)x+(x3x4)y=(x3y4x4y3)(y_4-y_3)x+(x_3-x_4)y=(x_3y_4-x_4y_3)

简洁表示为:

a2x+b2y=c2a_2x+b_2y=c_2

其中:

a2=y4y3b2=x3x4c2=x3y4x4y3a_2=y_4-y_3,b_2=x_3-x_4,c_2=x_3y_4-x_4y_3

解上述两个二元一次方程组得到根:

x=b2c1b1c2a1b2a2b1x=\frac {b_2c_1-b_1c_2}{a_1b_2-a_2b_1}
y=a1c2a2c1a1b2a2b1y=\frac {a_1c_2-a_2c_1}{a_1b_2-a_2b_1}

可以看到上述xyx、y的根分母是一致的为a1b2a2b1a_1b_2-a_2b_1,当分母a1b2a2b1=0a_1b_2-a_2b_1=0时,表示无解,在几何意义上就是两条直线没有交点,即L1//L2L_1//L_2

2.2代码演示

上述就是直线与直线相交的求解过程,假设坐标点位于X-Z平面内,下面代码实现:


        /// <summary>
        /// 计算平面中直线与直线的交点.
        /// </summary>
        /// <param name="point1">第一条直线上的点1</param>
        /// <param name="point2">第一条直线上的点2</param>
        /// <param name="point3">第二条直线上的点1</param>
        /// <param name="point4">第二条直线上的点2</param>
        /// <param name="point5">若两条直线平行,则返回该点</param>
        /// <returns>两条直线的交点.</returns>
        private Vector3 IntersectionOfStraightLine(
            Vector3 point1,
            Vector3 point2,
            Vector3 point3,
            Vector3 point4,
            Vector3 point5)
        {
            var a1 = point2.z - point1.z;
            var b1 = point1.x - point2.x;
            var c1 = (point1.x * point2.z) - (point2.x * point1.z);
            var a2 = point4.z - point3.z;
            var b2 = point3.x - point4.x;
            var c2 = (point3.x * point4.z) - (point4.x * point3.z);
            var denominator = (a1 * b2) - (a2 * b1);

            if (denominator != 0)
            {
                //两条直线不平行,有交点
                var x = ((b2 * c1) - (b1 * c2)) / denominator;
                var z = ((a1 * c2) - (a2 * c1)) / denominator;
                Debug.Log($"交点坐标为{x},{point1.y},{z}");
                return new Vector3(x, point1.y, z);
            }

            Debug.Log($"无交点坐标,两条直线不相交");
            return point5; 
        }


执行代码如下:

void Start()
    {
        Vector3 a = new Vector3(10, 0, 10);
        Vector3 b = new Vector3(10, 0, 0);
        Vector3 c = new Vector3(20, 0, 10);
        Vector3 d = new Vector3(20, 0, 0);

        //点a与原点的直线与点b与点c的直线平行,无交点
        var p1 = IntersectionOfStraightLine(Vector3.zero, a, b, c, Vector3.zero);

        //点a与点d的直线与点b与点c的直线相交
        var p2 = IntersectionOfStraightLine(a, d, b, c, Vector3.zero);
    }

输出结果如下: image.png

3.直线与直线相交-向量法

3.1求解过程

已知条件不变:直线L1L_1上的两点P1=(x1,y1)P_1=(x_1,y_1),P2=(x2,y2)P_2=(x_2,y_2),直线L2L_2上的两点P3=(x3,y3)P_3=(x_3,y_3),P4=(x4,y4)P_4=(x_4,y_4),求两条直线的交点I1I_1。如下图所示: image.png

过点P1P2P_1、P_2做垂线分别交于直线P3P4P_3、P_4,交点为O1O2O_1、O_2,连接点P1P3P_1、P_3P2P3P_2、P_3,设向量:

P1P2=P2P1P3P4=P4P3P3P1=P1P3P3P2=P2P3\overrightarrow{P_1P_2}=P_2-P_1\\ \overrightarrow{P_3P_4}=P_4-P_3\\ \overrightarrow{P_3P_1}=P_1-P_3\\ \overrightarrow{P_3P_2}=P_2-P_3\\

观察可知,上侧平行四边形的面积:

S1=P3P4×P1O1S_1=\lVert \overrightarrow{P_3P_4} \rVert \times P_1O_1

下侧平行四边形的面积:

S2=P3P4×P2O2S_2=\lVert \overrightarrow{P_3P_4} \rVert \times P_2O_2

设两个平行四边形的面积的比值tt等于:

t=S1S2=P1O1P2O2t=\frac {S_1}{S_2}=\frac {P_1O_1}{P_2O_2}

由向量的叉乘可知,两个向量的叉乘得到的向量的模长等于这两个向量围成的平行四边形的面积,所以平行四边形的面积可通过叉乘得到:

S1=P3P4×P3P1S2=P3P4×P3P2S_1= \overrightarrow{P_3P_4} \times \overrightarrow{P_3P_1} \\ S_2=\overrightarrow{P_3P_4} \times \overrightarrow{P_3P_2}

那么,求到两个平行四边形面积的比值tt,我们怎么求的交点I1I_1呢?由图可知,三角形P1O1I1\vartriangle P_1O_1I_1与三角形P2O2I1\vartriangle P_2O_2I_1是相似三角形,可以得到以下比例关系:

P1I1P2I1=P1O1P2O2=S1S2=t\frac {P_1I_1}{P_2I_1}=\frac {P_1O_1}{P_2O_2}=\frac {S_1}{S_2}=t

知道了线段P1I1P_1I_1与线段P2I1P_2I_1的比值,且知道向量P1P2\overrightarrow{P_1P_2}的模长及单位向量,就能求出线段P1I1P_1I_1的长度,从而求出点I1I_1的坐标。 线段P1I1P_1I_1的长度为:

P1I1=tt+1P1P2P_1I_1=\frac {t}{t+1}\lVert \overrightarrow{P_1P_2} \rVert

设向量P1P2\overrightarrow{P_1P_2}的单向向量为:

P1P2Norm=P1P2P1P2\overrightarrow{P_1P_2}_{Norm}=\frac {\overrightarrow{P_1P_2}}{\lVert \overrightarrow{P_1P_2} \rVert}

I1I_1的坐标为:

I1=P1+P1P2NormP1I1I_1=P_1+\overrightarrow{P_1P_2}_{Norm}P_1I_1

3.2代码演示

我们在Unity脚本中利用向量类Vector3实现上面向量法计算交点的方法,如下:

    /// <summary>
    /// 计算直线与直线的交点-向量法
    /// </summary>
    /// <param name="point1">第一条直线上的点1</param>
    /// <param name="point2">第一条直线上的点2</param>
    /// <param name="point3">第二条直线上的点1</param>
    /// <param name="point4">第二条直线上的点2</param>
    /// <returns>两条直线的交点.</returns>
    private Vector3 IntersectionOfLine(
        Vector3 point1,
        Vector3 point2,
        Vector3 point3,
        Vector3 point4)
    {
        var d1 = point2 - point1;
        var d2 = point4 - point3;
        
        //通过点积求的两个向量夹角,判断是否平行
        var cos = Vector3.Dot(d1, d2) / (d1.magnitude * d2.magnitude); 
        if (Math.Abs(cos) - 1 == 0)
        {
            Debug.Log($"无交点坐标,两条直线不相交");
            return Vector3.zero;
        }

        var d3 = point1 - point3;
        var d4 = point2 - point3;
        var d1Norm = d1 / d1.magnitude;                                                 //d1的单位向量
        var t = Vector3.Cross(d2, d3).magnitude / Vector3.Cross(d2, d4).magnitude;      //面积的比值t
        var result = point1 + (t / (t + 1) * d1.magnitude * d1Norm);                    //交点坐标

        Debug.Log($"采用向量法求得交点坐标为{result.x},{result.y},{result.z}");
        return result;
    }

执行代码如下,将方程组求解法与向量法的结果做下比较:

void Start()
    {
        Vector3 a = new Vector3(10, 0, 10);
        Vector3 b = new Vector3(10, 0, 0);
        Vector3 c = new Vector3(20, 0, 10);
        Vector3 d = new Vector3(20, 0, 0);

        //点a与原点的直线与点b与点c的直线平行,无交点
        var p1 = IntersectionOfStraightLine(Vector3.zero, a, b, c, Vector3.zero);

        //点a与点d的直线与点b与点c的直线相交
        var p2 = IntersectionOfStraightLine(a, d, b, c, Vector3.zero);

        //点a与原点的直线与点b与点c的直线平行,无交点
        var p3 = IntersectionOfLine(Vector3.zero, a, b, c);

        //点a与点d的直线与点b与点c的直线相交
        var p4 = IntersectionOfLine(a, d, b, c);
    }

输出结果如下: image.png

4.结语

上面我们介绍了两种在Unity平面内两条直线相交的交点的求解方法,向量法的复杂度感觉稍微简单点,具体运用过程中就看个人喜好。后面有机会继续介绍直线与圆的相交性检测!若有不足之处,望大家批评指正。