Этот ответ основан на идеях из проективной геометрии.
Вычислить перекрестное произведение (Ax, Ay, 1) × (Bx, By, 1) = (u, v, w).Результирующий вектор описывает линию, соединяющую А и В: он имеет уравнение ux + vy + w = 0.Но вы также можете интерпретировать (u, v, 0) как бесконечно удаленную точку в направлении, перпендикулярном этой линии.Делая еще одно перекрестное произведение, вы получаете линию, соединяющую точку с точкой P: (u, v, 0) × (Px, Py, 1).И чтобы пересечь эту линию с линией AB, вы делаете другое перекрестное произведение: ((u, v, 0) × (Px, Py, 1)) × (u, v, w).Результатом будет однородный координатный вектор (x, y, z), из которого вы можете прочитать координаты этой ближайшей точки как (x / z, y / z).
Возьмите все вместе, и вы получитеследующая формула:
Используя систему компьютерной алгебры, вы можете найти следующие координаты:
x = ((Ax - Bx)*Px + (Ay - By)*Py)*(Ax - Bx) + (Ay*Bx - Ax*By)*(Ay - By)
y = -(Ay*Bx - Ax*By)*(Ax - Bx) + ((Ax - Bx)*Px + (Ay - By)*Py)*(Ay - By)
z = (Ax - Bx)^2 + (Ay - By)^2
Как вы заметили, естьмного повторяющихся терминов.Придумывая (в значительной степени произвольные) имена для них, вы можете получить следующий конечный результат, написанный в псевдокоде:
dx = A.x - B.x
dy = A.y - B.y
det = A.y*B.x - A.x*B.y
dot = dx*P.x + dy*P.y
x = dot*dx + det*dy
y = dot*dy - det*dx
z = dx*dx + dy*dy
zinv = 1/z
return new Point(x*zinv, y*zinv)
Преимущества этого подхода:
- Нет различий в регистре
- без квадратных корней
- только одно деление