数学中表示直线的方式有很多种,其中参数方程式可以表示任意直线,并且其非常适合用于显示线段,表达式为用三个参数分别表示直线上的一个已知点以及参数t,即:
假设给定两个点(x1,y1)、(x2,y2),只需要其满足:
并且将t限制在[0,1]的范围内,就可以表示端点(x1,y1)、(x2,y2)的线段。
说明:
为了简化描述,设两条线段的端点分别为(x1,y1)、(x2,y2)以及(x3,y3)、(x4,y4)。初中数学我们学习过,平面上两条直线如果不平行,那么他们一定相交,并且有唯一的交点。下面分别从平行情况和不平行情况进行分析。
不平行情况:
对于线段1其参数方程为:
对于线段2其参数方程为:
联立两个方程组求出交点:
这是一个普通的二元一次方程组,我们可以得出其解为:
判断交点是否在线段上,只需要判断,即可。
平行情况:
检查斜率可以判断两条直线是否平行。即当
满足时,直线平行。但当线段与y轴平行时,此时分母为0。为了避免这种情况。我们可以这么写:
当两条线段平行之后,会有两种情况出现;
1、两条线段不在同一条直线上,没有交点。
2、两条线段在同一条直线上,此时可能会有多个交点。但最优的交点一定是两条直线四个端点中的一个。
即我们需要判断:
1、(x3,y3)是否在线段(x1,y1)~(x2,y2)上。
2、(x4,y4)是否在线段(x1,y1)~(x2,y2)上。
3、(x1,y1)是否在线段(x3,y3)~(x4,y4)上。
4、(x2,y2)是否在线段(x3,y3)~(x4,y4)上。
c++代码实现如下:
class Solution
{
public:
bool IsInside(cv::Point2d lineStart, cv::Point2d lineEnd, cv::Point2d point)
{
return (lineStart.x == lineEnd.x || (std::min(lineStart.x, lineEnd.x) <= point.x && point.x <= std::max(lineStart.x, lineEnd.x))) &&
(lineStart.y == lineEnd.y || (std::min(lineStart.y, lineEnd.y) <= point.y && point.y <= std::max(lineStart.y, lineEnd.y)));
}
/// <summary>
/// 线段与线段之间的交点
/// </summary>
/// <param name="line1Start">线段1起点</param>
/// <param name="line1End">线段1终点</param>
/// <param name="line2Start">线段2起点</param>
/// <param name="line2End">线段2终点</param>
/// <returns></returns>
cv::Point2d Intersection2Lines(cv::Point2d line1Start, cv::Point2d line1End, cv::Point2d line2Start, cv::Point2d line2End)
{
double x1 = line1Start.x, y1 = line1Start.y;
double x2 = line1End.x, y2 = line1End.y;
double x3 = line2Start.x, y3 = line2Start.y;
double x4 = line2End.x, y4 = line2End.y;
cv::Point2d intersectP;
// 判断是否平行
if ((y4 - y3) * (x2 - x1) == (y2 - y1) * (x4 - x3))
{
// 若共线
if ((y2 - y1) * (x3 - x1) == (y3 - y1) * (x2 - x1)) {
// 判断 (x3, y3) 是否在「线段」(x1, y1)~(x2, y2) 上
if (IsInside(line1Start, line1End, line2Start)) {
return line2Start;
}
// 判断 (x4, y4) 是否在「线段」(x1, y1)~(x2, y2) 上
if (IsInside(line1Start, line1End, line2End)) {
return line2End;
}
// 判断 (x1, y1) 是否在「线段」(x3, y3)~(x4, y4) 上
if (IsInside(line2Start, line2End, line1Start)) {
return line1Start;
}
// 判断 (x2, y2) 是否在「线段」(x3, y3)~(x4, y4) 上
if (IsInside(line2Start, line2End, line2End)) {
return line2End;
}
}
// 在平行时,其余的所有情况都不会有交点
}
else {
// 联立方程得到 t1 和 t2 的值
double t1 = (double)(x3 * (y4 - y3) + y1 * (x4 - x3) - y3 * (x4 - x3) - x1 * (y4 - y3)) / ((x2 - x1) * (y4 - y3) - (x4 - x3) * (y2 - y1));
double t2 = (double)(x1 * (y2 - y1) + y3 * (x2 - x1) - y1 * (x2 - x1) - x3 * (y2 - y1)) / ((x4 - x3) * (y2 - y1) - (x2 - x1) * (y4 - y3));
// 判断 t1 和 t2 是否均在 [0, 1] 之间
if (t1 >= 0.0 && t1 <= 1.0 && t2 >= 0.0 && t2 <= 1.0) {
intersectP = cv::Point2d(x1 + t1 * (x2 - x1), y1 + t1 * (y2 - y1));
}
}
return intersectP;
}
};