Как повернуть вектор на заданный угол и как сделать поворот на 90° без тригонометрии?
Нужно повернуть точку/вектор вокруг начала координат. Для произвольного угла понятно, что нужны синус и косинус, а вот поворот ровно на 90° хочется делать в целых числах, без потери точности. Как это записать?
2 ответа
Поворот вектора (x, y) на угол θ против часовой стрелки задаётся матрицей поворота:
x' = x·cos θ − y·sin θ
y' = x·sin θ + y·cos θ
// поворот на угол ang (радианы), CCW. double, неизбежно
pair<double,double> rotate(double x, double y, double ang) {
double c = cos(ang), s = sin(ang);
return { x * c - y * s, x * s + y * c };
}
Но для углов, кратных 90°, синус и косинус — это ±1 и 0, поэтому поворот делается точно в целых числах:
P rot90(const P& v) { return { -v.y, v.x }; } // +90° CCW
P rot270(const P& v) { return { v.y, -v.x }; } // -90° (или +270°) CCW
P rot180(const P& v) { return { -v.x, -v.y }; } // 180°
Поворот на 90° CCW переводит (x, y) в (−y, x) — это прямо следует из матрицы при θ=90°. Используй его, когда нужна перпендикуляр-нормаль к отрезку или поворот в задачах на сетке: остаёшься в long long, без double. Все операции O(1).
Полезный приём: нормаль (перпендикуляр) к вектору — это его поворот на 90°. Если у тебя ребро AB и нужна внешняя/внутренняя нормаль, бери rot90(B - A) — получишь перпендикулярный вектор той же длины, в целых числах. Дальше знак (внутрь/наружу) определяешь через cross с любой внутренней точкой.
И предупреждение про произвольный угол: cos/sin дают double, и многократные повороты накапливают ошибку. Если нужно повернуть много раз на один угол, посчитай c и s один раз и применяй матрицу, а не вызывай cos в цикле — и быстрее, и стабильнее. Для целочисленных задач старайся свести всё к поворотам на 90°.