求两条直线交点

本篇文章提供一个简单版本的求两条直线交点的方法,基于面向对象



输入输出

输入:直线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 d 1×d 2=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) d y2(xx3)=d x2(yy3),那么只需要把 ( x 1 , y 1 ) (x1,y1) (x1,y1) 代入判断等式是否成立即可,下面是这个表示的推导过程。
我们其实可以把直线表示成等式组:

  • x 3 + t d ⃗ x 2 = x x_3 + t\vec{d}_x^2=x x3+td x2=x
  • y 3 + t d ⃗ y 2 = y y_3 + t\vec{d}_y^2=y y3+td y2=y

那么由上面的等式组其实可以得到 t t t 的两种表示:

  • t = x − x 3 d ⃗ x 2 t = \frac{x - x_3}{\vec{d}_x^2} t=d x2xx3
  • t = y − y 3 d ⃗ y 2 t = \frac{y-y_3}{\vec{d}_y^2} t=d y2yy3

其实把 t t t 消去就可以得到上面直线表示,但这里需要注意的特殊情况就是 d ⃗ x 2 = 0 \vec{d}_x^2=0 d x2=0 d ⃗ y 2 = 0 \vec{d}_y^2=0 d y2=0

如果 d ⃗ x 2 = 0 \vec{d}_x^2=0 d x2=0 直线就是一条垂直于x轴的直线,如果 d ⃗ y 2 = 0 \vec{d}_y^2=0 d y2=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) d y2(xx3)=d x2(yy3)

找到交点

如果前面的判断都没通过,那么这两条直线相交,我们要找到它们的交点。
那么其实就是求解下面的等式组:


也可以表示成矩阵形式

这里的 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=d y1
  • b 1 = − d ⃗ x 1 b_1=-\vec{d}_x^1 b1=d x1
  • 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=d y1x1d x1y1
  • a 2 = d ⃗ y 2 a_2=\vec{d}_y^2 a2=d y2
  • b 2 = − d ⃗ x 2 b_2=-\vec{d}_x^2 b2=d x2
  • 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=d y2x3d x2y3

这里可以通过克莱姆法则求解:

  1. 计算系数矩阵的行列式 D D D

    如果 D = 0 D=0 D=0,则方程组无唯一解(也就是直线平行或重合)

  2. 计算 x x x 的行列式 D x D_x Dx,用常熟项替换 x x x 的系数

  3. 同理计算 y y y的行列式 D y D_y Dy
    . 在这里插入图片描述

  4. 求解 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
}

参考资料

克拉默法则证明(Cramer’s Rule)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值