Подтвердить что ты не робот

Прямой способ вычисления угла по часовой стрелке между двумя векторами

Я хочу узнать угол по часовой стрелке между двумя векторами (2D, 3D).

Классический способ с точечным произведением дает мне внутренний угол (0-180 градусов), и мне нужно использовать некоторые операторы if, чтобы определить, является ли результат нужным мне углом или его дополнением.

Вы знаете прямой способ вычисления угла по часовой стрелке?

4b9b3361

Ответ 1

2D чехол

Точно так же, как произведение точек пропорционально косинусу угла, детерминант пропорционален его синусу. Таким образом, вы можете вычислить угол следующим образом:

dot = x1*x2 + y1*y2      # dot product between [x1, y1] and [x2, y2]
det = x1*y2 - y1*x2      # determinant
angle = atan2(det, dot)  # atan2(y, x) or atan2(sin, cos)

Ориентация этого угла совпадает с ориентацией системы координат. В левосторонней системе координат, т.е. X направлен вправо, а y вниз, как обычно для компьютерной графики, это будет означать, что вы получите положительный знак для углов по часовой стрелке. Если ориентация системы координат математическая с y вверх, вы получите углы против часовой стрелки, как это принято в математике. Изменение порядка входов изменит знак, поэтому, если вы недовольны этими знаками, просто поменяйте местами входы.

3D чехол

В 3D два произвольно расположенных вектора определяют свою ось вращения, перпендикулярную обоим. Эта ось вращения не имеет фиксированной ориентации, что означает, что вы также не можете однозначно определить направление угла поворота. Одно общее правило - всегда разрешать углы положительно и ориентировать ось таким образом, чтобы она соответствовала положительному углу. В этом случае произведение точек нормализованных векторов достаточно для вычисления углов.

dot = x1*x2 + y1*y2 + z1*z2    #between [x1, y1, z1] and [x2, y2, z2]
lenSq1 = x1*x1 + y1*y1 + z1*z1
lenSq2 = x2*x2 + y2*y2 + z2*z2
angle = acos(dot/sqrt(lenSq1 * lenSq2))

Самолет встроен в 3D

Один частный случай - это случай, когда ваши векторы не расположены произвольно, а лежат в плоскости с известным вектором нормалей n. Тогда ось вращения будет также в направлении n, и ориентация n будет фиксировать ориентацию для этой оси. В этом случае вы можете адаптировать 2D-вычисления, приведенные выше, включая n в определитель, чтобы сделать его размер 3 × 3.

dot = x1*x2 + y1*y2 + z1*z2
det = x1*y2*zn + x2*yn*z1 + xn*y1*z2 - z1*y2*xn - z2*yn*x1 - zn*y1*x2
angle = atan2(det, dot)

Одним из условий этого является то, что нормальный вектор n имеет единичную длину. Если нет, вам придется нормализовать это.

Как тройной продукт

Этот детерминант также может быть выражен как тройной продукт, как @Excrubulent указал в предлагаемом редактировании.

det = n · (v1 × v2)

Это может быть проще для реализации в некоторых API и дает другое представление о том, что здесь происходит: перекрестное произведение пропорционально синусу угла и будет лежать перпендикулярно плоскости, следовательно, будет кратным n. Таким образом, скалярное произведение будет в основном измерять длину этого вектора, но с правильным знаком, прикрепленным к нему.

Ответ 2

Чтобы вычислить угол, вам просто нужно вызвать atan2(v1.s_cross(v2), v1.dot(v2)) для 2D-фрейма. Где s_cross - скалярный аналог перекрестного производства (подписанная площадь параллелограмма). Для 2D-случая это будет производство клина. Для 3D-случая вам нужно определить вращение по часовой стрелке, поскольку с одной стороны плоскости по часовой стрелке находится одно направление, с другой стороны плоскости - другое направление =)

Изменить: это против часовой стрелки, угол по часовой стрелке находится прямо напротив

Ответ 3

Этот ответ такой же, как у MvG, но объясняет его по-другому (это результат моих усилий в попытке понять, почему работает решение MvG). Я публикую это на случай, если другие считают это полезным.

Угол против часовой стрелки theta от x до y, относительно точки зрения их заданной нормы n (||n|| = 1), задается выражением

atan2 (точка (n, крест (x, y)), точка (x, y))

(1) = atan2 (|| x || || y || sin (theta), || x || || y || cos (theta))

(2) = atan2 (sin (theta), cos (theta))

(3) = угол против часовой стрелки между осью x и вектором (cos (theta), sin (theta))

(4) = theta​​p >

где ||x|| обозначает величину x.

Шаг (1) следует, заметив, что

cross (x, y) = || x || || у || sin (theta) n,

и поэтому

точка (n, крест (x, y))

= точка (n, || x || || y || sin (theta) n)

= || x || || у || sin (theta) dot (n, n)

который равен

|| х || || у || грех (тета)

if ||n|| = 1.

Шаг (2) следует из определения atan2, отмечая, что atan2(cy, cx) = atan2(y,x), где c - скаляр. Шаг (3) следует из определения atan2. Шаг (4) следует из геометрических определений cos и sin.

Ответ 4

Скалярное (точка) произведение двух векторов позволяет получить косинус угла между ними. Чтобы получить "направление" угла, вы также должны вычислить кросс-продукт, он позволит вам проверить (через координату z) угол по часовой стрелке или нет (т.е. Вы должны извлекать его с 360 градусов или нет).

Ответ 5

Для двумерного метода вы можете использовать закон косинусы и метод "направления".

Чтобы вычислить угол сегмента P3: P1 по часовой стрелке до сегмента P3: P2.

 
    P1     P2

        P3
    double d = direction(x3, y3, x2, y2, x1, y1);

    // c
    int d1d3 = distanceSqEucl(x1, y1, x3, y3);

    // b
    int d2d3 = distanceSqEucl(x2, y2, x3, y3);

    // a
    int d1d2 = distanceSqEucl(x1, y1, x2, y2);

    //cosine A = (b^2 + c^2 - a^2)/2bc
    double cosA = (d1d3 + d2d3 - d1d2)
        / (2 * Math.sqrt(d1d3 * d2d3));

    double angleA = Math.acos(cosA);

    if (d > 0) {
        angleA = 2.*Math.PI - angleA;
    }

This has the same number of transcendental

как предложения выше, и только один более или менее с плавающей запятой.

используемые методы:

 public int distanceSqEucl(int x1, int y1, 
    int x2, int y2) {

    int diffX = x1 - x2;
    int diffY = y1 - y2;
    return (diffX * diffX + diffY * diffY);
}

public int direction(int x1, int y1, int x2, int y2, 
    int x3, int y3) {

    int d = ((x2 - x1)*(y3 - y1)) - ((y2 - y1)*(x3 - x1));

    return d;
}

Ответ 6

Если по "прямому пути" вы имеете в виду избегать оператора if, то я не думаю, что существует действительно общее решение.

Однако, если ваша конкретная проблема позволила бы потерять некоторую точность в области дискретизации углов, и вы в порядке с потерей времени в преобразованиях типов, вы можете сопоставить допустимый диапазон phi угла [-pi, pi] на допустимом диапазоне некоторых целочисленный тип. Тогда вы получите бесплатную дополнительную взаимодополняемость. Однако на практике я не использовал этот трюк. Скорее всего, затраты конверсий float-to-integer и integer-to-float перевешивают любую выгоду от прямоты. Лучше устанавливать приоритеты при написании autovectorizable или параллелизуемого кода, когда это вычисление угла сделано много.

Кроме того, если ваши детали проблемы таковы, что есть определенный более вероятный результат для направления углов, то вы можете использовать встроенные функции компиляторов для предоставления этой информации компилятору, чтобы он мог более эффективно оптимизировать ветвление. Например, в случае gcc эта функция __builtin_expect. Это несколько более удобно использовать, когда вы переносите его в такие макросы likely и unlikely (например, в ядре linux):

#define likely(x)      __builtin_expect(!!(x), 1)
#define unlikely(x)    __builtin_expect(!!(x), 0)

Ответ 7

Формула для угла по часовой стрелке, 2D случай, между 2 векторами, xa, ya и xb, yb.

Угол (vec.a-vec, b) = pi()/2 * ((1 + знак (ya)) * (1-знак (xa ^ 2)) - (1 + знак (yb)) * (1- знак (хь ^ 2)))

                        +pi()/4*((2+sign(ya))*sign(xa)-(2+sign(yb))*sign(xb))

                        +sign(xa*ya)*atan((abs(ya)-abs(xa))/(abs(ya)+abs(xa)))

                        -sign(xb*yb)*atan((abs(yb)-abs(xb))/(abs(yb)+abs(xb)))

Ответ 8

просто скопируйте и вставьте это.

angle = (acos((v1.x * v2.x + v1.y * v2.y)/((sqrt(v1.x*v1.x + v1.y*v1.y) * sqrt(v2.x*v2.x + v2.y*v2.y))))/pi*180);

пожалуйста ;-)