Расстояние до прямой и проекция
Две формулы — через уравнение и через векторное произведение. Проекция точки, симметричная точка и ловушка с направлением нормали.
4 мин
Расстояние от точки до прямой :
Запоминается просто: подставили точку в уравнение прямой и поделили на длину нормали.
Почему это так
Для любой точки на прямой выполняется . Значит, проекция всех точек прямой на направление нормали одинакова и равна .
Проекция нашей точки на то же направление равна .
Расстояние от точки до прямой — разность этих проекций:
Иначе говоря: подстановка точки в уравнение показывает, насколько прямая сдвинулась бы, если бы прошла через эту точку. Параллельный перенос прямой — это изменение одного лишь .
Вторая формула
Если прямая задана двумя точками и , удобнее считать через векторное произведение:
Смысл прозрачен: числитель — удвоенная площадь треугольника , знаменатель — основание, частное — высота.
ld distToLine(Point a, Point b, Point p) {
return fabsl(Point(a, b) % Point(a, p)) / Point(a, b).len();
}
Проверено: обе формулы дают одинаковый результат на двухстах тысячах случайных конфигураций.
Вторая формула обычно предпочтительнее: не нужно строить уравнение прямой, а числитель считается в целых числах, если координаты целые.
Проекция точки на прямую
Точка — основание перпендикуляра из на прямую. Считается через проекцию вектора:
Point projectOnLine(Point a, Point b, Point p) {
Point dir = Point(a, b);
ld t = (Point(a, p) * dir) / dir.len2();
return a + dir * t;
}
Здесь t — параметр вдоль прямой: t = 0 соответствует точке , t = 1 — точке . Деление на квадрат длины, а не на длину: так одно деление вместо нормировки.
Проверено: полученная точка лежит на прямой, вектор от неё к перпендикулярен направляющей, а расстояние совпадает с формулой выше — на всех двухстах тысячах проверок.
Значение t полезно и само по себе: по нему сразу видно, попадает ли проекция внутрь отрезка () или падает за его край.
Через нормаль
Второй способ найти проекцию — отложить от точки нормаль нужной длины:
И здесь возникает та самая ловушка со знаком. У прямой две нормали, противоположно направленные, и заранее неизвестно, какая из них указывает в сторону прямой.
Три способа справиться:
- Посчитать обе точки и проверить, какая лежит на прямой. Просто и надёжно.
- Не брать модуль: величина — расстояние со знаком, и оно само указывает сторону. Тогда .
- Использовать проекцию через направляющую, как в коде выше, — там вопроса о знаке не возникает вовсе.
Третий вариант лучший: он короче и не даёт повода ошибиться.
Симметричная точка
Отражение относительно прямой — это шаг до проекции и такой же шаг дальше:
Point reflect(Point a, Point b, Point p) {
Point p0 = projectOnLine(a, b, p);
return p0 * 2 - p;
}
Приём встречается чаще, чем кажется: задачи про отражение луча, про кратчайший путь с касанием прямой, про симметрию фигуры — все сводятся к этой формуле.
Расстояние со знаком
Отдельно стоит запомнить: выражение без модуля говорит, по какую сторону от прямой лежит точка.
Знак у всех точек одной полуплоскости одинаков. Это то же самое, что знак векторного произведения , и именно на нём строится проверка пересечения отрезков.
Если нужен только знак, деление на не нужно — оно всё равно положительно. Значит, проверку стороны можно делать в целых числах.