本篇文章提供一个简单版本的求两条直线交点的方法,基于面向对象
输入输出
输入:直线1上的两个点 ( x 1 , y 1 ) (x_1,y_1) (x1,y1), ( x 2 , y 2 ) (x_2,y_2) (x2,y2),直线2上的两个点 ( x 3 , y 3 ) (x_3,y_3) (x3,y3), ( x 4 , y 4 ) (x_4, y_4) (x4,y4)。
输出:直线错误输出 error
,直线平行输出 parallel
,直线重合输出 coincide
,其他情况输出交点坐标保留两位有效数字。
算法流程
下面讲解每一个子流程。
是否有误
有误的情况其实就是直线退化成点,比如对于直线1就是 x 1 = = x 2 & & y 1 = = y 2 x1==x2 \&\&y1==y2 x1==x2&&y1==y2,
我们后续在 Line
中加入 isDegenerate
方法判断是否是退化点。
是否平行
平行可以通过两条直线的方向向量叉乘得到,如果我们直线用 p ⃗ + t d ⃗ \vec{p}+t\vec{d} p+td 那么如果这两条直线平行等价于 d ⃗ 1 × d ⃗ 2 = 0 \vec{d}_1 \times \vec{d}_2 = 0 d1×d2=0
是否重合
重合的话就是在两条直线平行的基础上,判断直线1上的任意点是否在直线2上,如果在的话就是重合。当然也可以通过判断直线1上一点与直线2上的一点定义的直线是否和直线1/直线2平行。
这里主要说下通过判断直线1上的任意点是否在直线2上的方法。
比如判断直线1上的点 ( x 1 , y 1 ) (x1,y1) (x1,y1)是否在直线2上,首先直线2可以表示为 d ⃗ y 2 ( x − x 3 ) = d ⃗ x 2 ( y − y 3 ) \vec{d}_y^2(x-x_3)=\vec{d}_x^2(y-y3) dy2(x−x3)=dx2(y−y3),那么只需要把 ( x 1 , y 1 ) (x1,y1) (x1,y1) 代入判断等式是否成立即可,下面是这个表示的推导过程。
我们其实可以把直线表示成等式组:
- x 3 + t d ⃗ x 2 = x x_3 + t\vec{d}_x^2=x x3+tdx2=x
- y 3 + t d ⃗ y 2 = y y_3 + t\vec{d}_y^2=y y3+tdy2=y
那么由上面的等式组其实可以得到 t t t 的两种表示:
- t = x − x 3 d ⃗ x 2 t = \frac{x - x_3}{\vec{d}_x^2} t=dx2x−x3
- t = y − y 3 d ⃗ y 2 t = \frac{y-y_3}{\vec{d}_y^2} t=dy2y−y3
其实把 t t t 消去就可以得到上面直线表示,但这里需要注意的特殊情况就是 d ⃗ x 2 = 0 \vec{d}_x^2=0 dx2=0 或 d ⃗ y 2 = 0 \vec{d}_y^2=0 dy2=0。
如果 d ⃗ x 2 = 0 \vec{d}_x^2=0 dx2=0 直线就是一条垂直于x轴的直线,如果 d ⃗ y 2 = 0 \vec{d}_y^2=0 dy2=0直线就是一条垂直于y轴的直线。
最终可以得到直线2的表示 d ⃗ y 2 ( x − x 3 ) = d ⃗ x 2 ( y − y 3 ) \vec{d}_y^2(x-x_3)=\vec{d}_x^2(y-y3) dy2(x−x3)=dx2(y−y3)。
找到交点
如果前面的判断都没通过,那么这两条直线相交,我们要找到它们的交点。
那么其实就是求解下面的等式组:
也可以表示成矩阵形式
这里的 a 1 a_1 a1、 a 2 a_2 a2、 b 1 b_1 b1、 b 2 b_2 b2、 c 1 c_1 c1、 c 2 c_2 c2、 x x x、 y y y 可以分别表示如下
- a 1 = d ⃗ y 1 a_1=\vec{d}_y^1 a1=dy1
- b 1 = − d ⃗ x 1 b_1=-\vec{d}_x^1 b1=−dx1
- c 1 = d ⃗ y 1 x 1 − d ⃗ x 1 y 1 c_1=\vec{d}_y^1x_1-\vec{d}_x^1y_1 c1=dy1x1−dx1y1
- a 2 = d ⃗ y 2 a_2=\vec{d}_y^2 a2=dy2
- b 2 = − d ⃗ x 2 b_2=-\vec{d}_x^2 b2=−dx2
- c 2 = d ⃗ y 2 x 3 − d ⃗ x 2 y 3 c_2=\vec{d}_y^2x_3-\vec{d}_x^2y_3 c2=dy2x3−dx2y3
这里可以通过克莱姆法则求解:
-
计算系数矩阵的行列式 D D D
如果 D = 0 D=0 D=0,则方程组无唯一解(也就是直线平行或重合) -
计算 x x x 的行列式 D x D_x Dx,用常熟项替换 x x x 的系数
-
同理计算 y y y的行列式 D y D_y Dy
. -
求解 x x x 和 y y y
UML类图
代码
#include <iostream>
#include <iomanip>
using namespace std;
#define Square(x) ((x) * (x))
struct Point
{
double _x, _y;
Point() = delete;
Point(double x, double y) : _x(x), _y(y) {
}
Point(const Point& other) : _x(other._x), _y(other._y) {
}
double dist(const Point& other) const {
return sqrt(Square(_x - other._x) + Square(_y - other._y));
}
};
struct Direction
{
double _x, _y;
Direction() = delete;
Direction(const Point& pt1, const Point& pt2)
{
const double distance = pt1.dist(pt2);
if (distance == 0.0) // degenerate
{
_x = 0.0;
_y = 0.0;
}
else
{
_x = (pt1._x - pt2._x) / distance;
_y = (pt1._y - pt2._y) / distance;
}
}
double operator^(const Direction& other) const
{
return _x * other._y - _y * other._x;
}
};
struct Line
{
Point _pt;
Direction _dir;
Line() = delete;
Line(const Point& pt1, const Point& pt2) : _pt(pt1), _dir(pt1, pt2) {
}
bool isDegenerated() const
{
return _dir._x == 0.0 && _dir._y == 0.0;
}
bool isParallel(const Line& other) const
{
return (_dir ^ other._dir) == 0.0;
}
void findIntersection(const Line& other) const
{
if (isDegenerated() || other.isDegenerated())
{
cout << "error" << endl;
return;
}
if (isParallel(other))
{
// parallel
const double left = other._dir._y * (_pt._x - other._pt._x);
const double right = other._dir._x * (_pt._y - other._pt._y);
if (left == right)
{
// coincident
cout << "coincident" << endl;
}
else {
// just parallel
cout << "parallel" << endl;
}
return;
}
// have intersection
const double a1 = _dir._y;
const double b1 = -_dir._x;
const double c1 = _dir._y * _pt._x - _dir._x * _pt._y;
const double a2 = other._dir._y;
const double b2 = -other._dir._x;
const double c2 = other._dir._y * other._pt._x - other._dir._x * other._pt._y;
const double D = a1 * b2 - a2 * b1;
const double Dx = c1 * b2 - c2 * b1;
const double Dy = a1 * c2 - a2 * c1;
cout << fixed << setprecision(2) << Dx / D << " " << Dy / D << endl;
}
};
int main()
{
// case1: coincident
Point pt1{
0.0, 0.0 };
Point pt2{
1.0, 1.0 };
Line line1{
pt1, pt2 };
Point pt3{
3.0, 3.0 };
Point pt4{
2.0, 2.0 };
Line line2{
pt3, pt4 };
line1.findIntersection(line2);
// case2: just parallel
Point pt5{
2.0, 0.0 };
Point pt6{
3.0, 1.0 };
Line line3{
pt5, pt6 };
line1.findIntersection(line3);
// case3: intersect
Point pt7{
2.0, 0.0 };
Point pt8{
3.0, -1.0 };
Line line4{
pt7, pt8 };
line1.findIntersection(line4); // 1.00 1.00
}