点到直线上的投影
首先关于一个点与直线交点的问题。
我们首先确定点在直线外面。
做一个过P点垂直于L的直线,那么相交的点我们叫I点。
如果我们用y = kx + b来表示L直线,那么我们新的直线PI的表达式为y = (-1/K)x + b2。
由此我们知道,I这个点,既满足y = kx + b,也满足 y = (-1/k)x + b2;
通过一系列的推导,我们就可以得到b2 = P.y + 1/k * P.x;
得到这个值之后,我们两条直线的表达式都出来了,那么再通过I满足两条直线的条件,联立方程组,我们可以推导出来:
I.y = (b + b2)/2;
I.x = (I.y - b2) / (-1/k);
所以如果遇到求一个点在另外一条直线上的投影的问题时,我们可以先求出一个目标直线的K和b,然后推导出来b2,最后使用上面的公式就可以计算出来了。
下面附上计算代码C#版。
/// <summary>
/// 求一个点到直线的投影
/// </summary>
/// <param name="point">直线上的一个点</param>
/// <param name="k">直线斜率</param>
/// <param name="pOut">线外的指定点</param>
/// <param name="target">返回的投影点</param>
public void GetProjectivePoint(Vector2 start, Vector2 end, Vector2 pOut, out Vector2 target)
{
target = Vector2.zero;
float dx = end.x - start.x;
float dy = end.y - start.y;
if (dx == 0)
{
target.x = start.x;
target.y = pOut.y;
}
else if (dy == 0)
{
target.x = pOut.x;
target.y = start.y;
}
else
{
float k1 = dy / dx;
float k2 = -dx / dy;
float b1 = start.y - k1 * start.x;
float b2 = pOut.y - k2 * pOut.x;
target.x = (b2 - b1) / (k1 - k2);
target.y = k1 * target.x + b1;
//target.x = (float)((k * pOut.x + pOut.x / k + pOut.y - start.y) / (1 / k + k));
//target.y = (float)(-1 / k * (target.x - pOut.x) + pOut.y);
}
}
直线相交
另外一个问题,那么就是我们怎么样去计算直线之间的交点问题。
根据上面解决点到直线的投影问题,我们先把直线转化成y = kx + b的这种表示形式。
那么就能得到两个方程式,但是我们前提需要注意的问题是:
1、如果两条直线同时垂直一个方向轴的话,那么就没有交点或者两条直线是同一条直线。
2、如果是斜率都相等的情况下,那么也是没有交点或者有无数个交点的。
下面我们来说一下关于斜率不相等并且跟方向轴都不垂直也不平行的情况。
先把公式粘贴出来。
TargetPoint.x = -(L1.b - L2.b)/(L1.K - L2.K);
TargetPoint.y = L1.K * TargetPoint.x + L1.b;
这个公式怎么去推导出来的呢?
其实跟上面点到直线上的投影,理论上是一样的,都是通过联立两个二元一次方程组,
就可以得到相对应的内容了。
下面把代码粘贴出来C#:
/// <summary>
/// 求两条直线的交点
/// </summary>
/// <param name="l1">直线1</param>
/// <param name="l2">直线2</param>
/// <param name="point">交点</param>
/// <param name="inLine">交点所在的直线,
/// 0:不在两条线段上;1:在第一个线段当中;2:在第二个线段当中;3:在两个线段当中</param>
/// <returns></returns>
public bool CalLineIntersectionPoint(SGLine l1, SGLine l2, out Vector2 point, out int inLine)
{
point = Vector2.zero;
inLine = 0;
//同时平行于X
if (l1.m_ParallelX && l2.m_ParallelX)
{
return false;
}
//同时平行于Y
if (l1.m_ParallelY && l2.m_ParallelY)
{
return false;
}
//斜率相同
if (l1.m_LineSlope == l2.m_LineSlope)
{
return false;
}
point.x = -(l1.m_LineDeviation - l2.m_LineDeviation) / (l1.m_LineSlope - l2.m_LineSlope);
point.y = l1.m_LineSlope * point.x + l1.m_LineDeviation;
inLine += PointInLines(l1.m_LineStart, l1.m_LineEnd, point) ? 1 : 0;
inLine += PointInLines(l2.m_LineStart, l2.m_LineEnd, point) ? 2 : 0;
return true;
}
上面的代码当中,还返回了一个值,表示了这个交点是否在线段当中,在那个线段当中都已经明确返还回去了,既然多了个内容,那么就还有一个需要谈一下的,
关于一个点是否在线段当中的问题。
当然,最简单粗暴的方法就是x,y的判断,大于等于跟小于等于,然后就可以等到了,但是这里面运用了另外一个内容,点与线段两边的正负符号进行了一个求值,
首先我们排除相等的情况,接下来就是,目标点减去起点和终点的正负方向,然后相加,那么结果一定是一个零向量,那么就在线段当中,如果不是零向量,那么
就不在线段当中,当然,如果是正数,说明在线段的右边,如果是负数,那么就在线段的左边了。