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

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

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

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

Комментарии к вопросу (9)
Решение

2D случае

Просто, как [точка] продукт1 пропорциональна Косинусу угла, определитель это proprortional его синуса. Так что вы можете вычислить угол такой:

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)

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

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

Один особый случай-когда ваши векторы не являются произвольно, но лежат в плоскости с известным вектором нормали Н. Тогда ось вращения будет находиться в направлении Н, Как хорошо, и ориентация н будет фиксировать ориентацию этой оси. В этом случае, вы можете адаптировать выше 2Д вычисления, в том числе и Н в определитель, чтобы сделать ее размером 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 имеет длину блока. Если нет, вы'll должны нормализовать его.

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

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

det = n · (v1 × v2)

Это может быть легче реализовать в некоторые API, и дает разные точки зрения на то, что's идя дальше здесь: векторное произведение пропорционально синусу угла, и будет лежать перпендикулярно к плоскости, следовательно, кратна Н. Поэтому скалярное произведение будет в основном измеряем длину этого вектора, но с правильной надписью прилагается к нему.

Комментарии (13)

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

Edit: это угол против часовой стрелки, угол по часовой стрелке - как раз наоборот

Комментарии (7)

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

Анти-по часовой стрелке угол в <код>У тета</код> от <код>х</код и GT; в <код>г</код> в отношении зрения их дали нормальный на <код>п</код> (в<код> У||Н|| = 1</код и GT;), дается

инструмент atan2( точка(Н, крестиком(х,г)), точка(Х,Y) )

У (1) = инструмент atan2( ||х|| ||г|| грех(тета),  ||х|| ||г|| в COS(тета) )

У (2) = инструмент atan2( грех(тета), в COS(тета) )

У (3) = анти-по часовой стрелке угол между осью X и вектором (COS(тета), грех(тета))

У (4) = тета

где <код> У||Х||</код> обозначает величины в <код>х</код>.

Шаг (1) Следует отметить, что

крест(х,г) = ||х|| ||г|| грех(тета) Н

и так

дот(п, крест(Х,Y))

= точка(н ||х|| ||г|| грех(тета) Н)

У = ||х|| ||г|| грех(тета) точка(н, н)

которая равна

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

если в <код>||Н|| = 1</код>.

Шаг (2) следует из определения состояния <код>инструмент atan2</код>, отметив, что в <код>инструмент atan2(ТИЦ, СХ) = инструмент atan2(у,Х)</код>, где <код>С в< код> есть скаляр. Шаг (3) следует из определения состояния <код>инструмент atan2</код>. Шаг (4) следует из геометрического определения состояния <код>потому что в< код> и <код>грех</код>.

Комментарии (0)

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

Комментарии (6)

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

Для расчета угла сегмента П3:П1 подметание по часовой стрелке в сегменте Р3:Р2. в <предварительно> П1 П2

    P3

</пред> двойной D = направление(Х3, У3, Х2, У2, Х1, У1);

    // 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;
}
Комментарии (0)

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

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

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

#define likely(x)      __builtin_expect(!!(x), 1)
#define unlikely(x)    __builtin_expect(!!(x), 0)
Комментарии (0)

Формула для угла по часовой стрелке,2Д, между 2 векторами, ха,УА и ХВ,ИБ.

Угол(ВМК.а-ВМК,б)=пи()/2((1+знак(я)) (1-знак(ХД^2))-(1+знак(ИБ))* (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)))
Комментарии (0)

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

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);

вы'вновь добро пожаловать ;-)

Комментарии (1)