Поворот точки вокруг другой точки (2D)


139

Я пытаюсь сделать карточную игру, где карты разветвляются. Прямо сейчас, чтобы отобразить его, я использую Allegro API, который имеет функцию:

al_draw_rotated_bitmap(OBJECT_TO_ROTATE,CENTER_X,CENTER_Y,X
        ,Y,DEGREES_TO_ROTATE_IN_RADIANS);

так что с этим я могу легко сделать эффект вентилятора. Проблема в том, что вы знаете, какая карта находится под мышью. Чтобы сделать это, я подумал о проведении теста столкновения полигонов. Я просто не уверен, как повернуть 4 точки на карте, чтобы составить многоугольник. Мне в основном нужно сделать ту же операцию, что и Аллегро.

Например, 4 пункта карты:

card.x

card.y

card.x + card.width

card.y + card.height

Мне нужна функция вроде:

POINT rotate_point(float cx,float cy,float angle,POINT p)
{
}

Спасибо

Ответы:


331

Сначала вычтите опорную точку (cx,cy), затем поверните ее, затем снова добавьте точку.

Непроверенные:

POINT rotate_point(float cx,float cy,float angle,POINT p)
{
  float s = sin(angle);
  float c = cos(angle);

  // translate point back to origin:
  p.x -= cx;
  p.y -= cy;

  // rotate point
  float xnew = p.x * c - p.y * s;
  float ynew = p.x * s + p.y * c;

  // translate point back:
  p.x = xnew + cx;
  p.y = ynew + cy;
  return p;
}

45
Отличный ответ. Для записи, вы получили правильный поворот в первый раз.
н.коллинз

@synchronizer точно такой же, просто используйте ваши процедуры вычитания / сложения точек и векторную матричную функцию для вращения.
Нильс Пипенбринк

8
Для неосторожных может быть полезно упомянуть, что sin и cos могут ожидать, что угол будет выражен в радианах.
15ee8f99-57ff-4f92-890c-b56153

Прав ли я, ожидая, что положительные значения угла будут выполнять вращение вправо (по часовой стрелке), а предоставление отрицательного значения будет выполнять его влево (против часовой стрелки)? Или против часовой стрелки - более сложная операция (то есть вычисление обратного угла и затем вращение по часовой стрелке на эту величину)? Я видел тонну страниц, дающих такую ​​же формулу, но никто никогда не считает нужным говорить о направленности по отношению к значениям ввода / вывода ...
NetXpert

72

Если вы повернете точку (px, py)вокруг точки (ox, oy)на угол тета, вы получите:

p'x = cos(theta) * (px-ox) - sin(theta) * (py-oy) + ox

p'y = sin(theta) * (px-ox) + cos(theta) * (py-oy) + oy

это простой способ повернуть точку в 2D.


7
Вам нужно перевести обратно после поворота. так что решение будет таким: p'x + = ox
hAlE

57

Система координат на экране левосторонняя, то есть координата x увеличивается слева направо, а координата y увеличивается сверху вниз. Начало координат O (0, 0) находится в верхнем левом углу экрана.

введите описание изображения здесь

По часовой стрелке , вращение вокруг начала координат точки с координатами (х, у) задается следующими уравнениями:

введите описание изображения здесь

где (x ', y') - координаты точки после поворота, а угол тета - угол поворота (должен быть в радианах, т.е. умножен на: PI / 180).

Чтобы выполнить вращение вокруг точки, отличной от начала координат O (0,0), скажем, точка A (a, b) (точка поворота). Сначала мы переводим точку, которую нужно повернуть, т.е. (x, y) обратно в начало координат, вычитая координаты точки поворота, (x - a, y - b). Затем мы выполняем вращение и получаем новые координаты (x ', y') и, наконец, переводим точку обратно, добавляя координаты точки поворота к новым координатам (x '+ a, y' + b).

После приведенного выше описания:

2D вращение по часовой стрелке тета-градусов точки (x, y) вокруг точки (a, b) :

Используя прототип вашей функции: (x, y) -> (px, py); (a, b) -> (cx, cy); тета -> угол:

POINT rotate_point(float cx, float cy, float angle, POINT p){

     return POINT(cos(angle) * (p.x - cx) - sin(angle) * (p.y - cy) + cx,
                  sin(angle) * (p.x - cx) + cos(angle) * (p.y - cy) + cy);
}

29
float s = sin(angle); // angle is in radians
float c = cos(angle); // angle is in radians

Для вращения по часовой стрелке:

float xnew = p.x * c + p.y * s;
float ynew = -p.x * s + p.y * c;

Для вращения против часовой стрелки:

float xnew = p.x * c - p.y * s;
float ynew = p.x * s + p.y * c;

Что cи s?
TankorSmash

1
@TankorSmash это определено вышеc = cos(angle)
nycynik

2

Это ответ Нильса Пипенбринка, но реализованный в c # fiddle.

https://dotnetfiddle.net/btmjlG

using System;

public class Program
{
    public static void Main()
    {   
        var angle = 180 * Math.PI/180;
        Console.WriteLine(rotate_point(0,0,angle,new Point{X=10, Y=10}).Print());
    }

    static Point rotate_point(double cx, double cy, double angle, Point p)
    {
        double s = Math.Sin(angle);
        double c = Math.Cos(angle);
        // translate point back to origin:
        p.X -= cx;
        p.Y -= cy;
        // rotate point
        double Xnew = p.X * c - p.Y * s;
        double Ynew = p.X * s + p.Y * c;
        // translate point back:
        p.X = Xnew + cx;
        p.Y = Ynew + cy;
        return p;
    }

    class Point
    {
        public double X;
        public double Y;

        public string Print(){
            return $"{X},{Y}";
        }
    }
}

PS: Очевидно, я не могу комментировать, поэтому я обязан опубликовать это как ответ ...

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.