Касательные к окружности
Точки касания из внешней точки находятся без тригонометрии: проекция и сдвиг вдоль перпендикуляра.
2 мин
Из точки вне окружности к ней проходят две касательные, из точки на окружности - одна, изнутри - ни одной. Найти точки касания можно одной формулой, без углов.
Построение
Пусть - центр, - радиус, - точка, , .
Точки касания лежат на окружности и видны из под прямым углом к касательной. Их общая хорда перпендикулярна и удалена от центра на . Значит:
а сами точки - это , где .
Long d2 = dot(d, d);
if (d2 < (Long)r * r) { нет касательных }
if (d2 == (Long)r * r) { одна, и это сама точка P }
Real base = (Real)r * r / d2;
Real h = (Real)r * std::sqrt((Real)(d2 - (Long)r * r)) / d2;
P1 = {C.x + base * d.x - h * d.y, C.y + base * d.y + h * d.x};
P2 = {C.x + base * d.x + h * d.y, C.y + base * d.y - h * d.x};
Обратите внимание: сравнение «внутри или снаружи» делается в целых числах - против . Ни корня, ни эпсилона здесь не нужно, и граничный случай «точка ровно на окружности» определяется точно.
Почему не через углы
Школьный способ - найти угол и повернуть вектор. Он работает, но требует двух тригонометрических вызовов и матрицы поворота, а на границе (точка почти на окружности) аргумент арккосинуса из-за погрешности может выскочить за единицу.
Формула выше состоит из умножений и одного корня, причём подкоренное выражение целое и неотрицательное по построению.
Общие касательные к двум окружностям
Задача сводится к предыдущей приёмом «сдуть окружности». Чтобы найти внешние касательные, уменьшим оба радиуса на : одна из окружностей превратится в точку, и останется провести касательные из точки к другой окружности, а потом сдвинуть прямые обратно на .
Для внутренних касательных то же самое, но радиусы меняются в разные стороны: и .