用向量叉乘求直線交點

求解方法

兩個技巧:

  • 向量來表示點,則可以用向量加法來表示點的平移
  • 點和向量來表示直線,這樣可以處理斜率無窮大的情況。

如圖所示,有兩條直線交於點 \(I\)。我們用點 \(p_1\) 和向量 \(\vec v_1\) 來表示直線 1,用點 \(p_2\) 和向量 \(\vec v_2\) 來表示直線 2。

把點看成向量,則有 \(\mathbf I=\mathbf p_1+t\mathbf v_1\)(用向量加法表示點的平移),現在我們的任務就是求出標量 \(t\)。所謂用向量叉乘來求直線交點,就是利用 \(\overrightarrow{Ip_2}\times\vec v_2=\vec 0\)(兩向量平行)這個式子求出 \(t\)

在開始推導之前,先複習一下向量叉乘的幾條運算規律:

  • 加法的左分配律:\(\mathbf{a} \times (\mathbf{b} + \mathbf{c}) = \mathbf{a} \times \mathbf{b} + \mathbf{a} \times \mathbf{c}\)
  • 加法的右分配律:\((\mathbf{a} + \mathbf{b}) \times \mathbf{c} = \mathbf{a} \times \mathbf{c} + \mathbf{b} \times \mathbf{c}\)
  • 標量乘法:\((\lambda\mathbf{a})\times\mathbf{b} = \lambda(\mathbf{a}\times\mathbf{b}) = \mathbf{a}\times(\lambda\mathbf{b})\)

下面我們來求解標量 \(t\),推導過程如下:

\[\begin{array}{rcl}
(\mathbf{p}_2 – \mathbf{I}) \times \mathbf{v}_2 &=& \mathbf{0}\\
(\mathbf{p}_2 – (\mathbf{p}_1 + t\mathbf{v}_1)) \times \mathbf{v}_2 &=& \mathbf{0}\\
(\mathbf{p}_2 – \mathbf{p}_1) \times \mathbf{v}_2 – t\mathbf{v}_1 \times \mathbf{v}_2 &=& \mathbf{0}\\
(\mathbf{p}_2 – \mathbf{p}_1) \times \mathbf{v}_2 &=& t(\mathbf{v}_1 \times \mathbf{v}_2)\\
t &=& \frac{|(\mathbf{p}_2 – \mathbf{p}_1) \times \mathbf{v}_2|}{|\mathbf{v}_1 \times \mathbf{v}_2|}
\end{array}
\]

好了,現在我們求出了標量 \(t\),接下來用 \(\mathbf I=\mathbf p_1+t\mathbf v_1\) 這個式子計算出交點 \(I\) 的坐標即可。

順便一提,這種「將點和向量統一表示」的思想在電腦圖形學中有很重要的應用,即齊次坐標。

程式碼

首先我們需要寫一個向量類。

注意,程式碼中的 cross 函數求的是向量積的模,所以返回的是個標量。

//二維向量
struct Vec
{
    double x, y;
    Vec() = default;
    Vec(double x, double y) : x(x), y(y) {}
    double len2() const { return x * x + y * y; }
    double len() const { return sqrt(len2()); }
    Vec operator+(const Vec &b) const { return Vec(x + b.x, y + b.y); }
    Vec operator-(const Vec &b) const { return Vec(x - b.x, y - b.y); }
    Vec operator*(double t) const { return Vec(x * t, y * t); }
    Vec operator/(double t) const { return Vec(x / t, y / t); }
    Vec operator-() const { return Vec(-x, -y); }
};

//向量積的模
double cross(const Vec &a, const Vec &b) { return a.x * b.y - b.x * a.y; }

求解兩直線交點的程式碼。程式碼相當簡潔,而且可以應對包括直線斜率無窮大在內的所有情況。

//求解兩直線交點
//p1、v1 用於表示直線 1,p2、v2 用於表示直線 2
Vec intersection(const Vec &p1, const Vec &v1, const Vec &p2, const Vec &v2)
{
    double t = cross((p2 - p1), v2) / cross(v1, v2);
    return p1 + v1 * t;
}