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

Мне нужен подписанный угол поворота между двумя векторами Va и Vb, лежащими в одной и той же трехмерной плоскости и имеющими одно и то же происхождение, зная, что:

  • Плоскость, контактирующая с обоими векторами, является произвольной и не параллельна XY или любой другой из кардинальных плоскостей
  • Vn - плоская нормаль
  • Оба вектора вместе с нормалью имеют одинаковое происхождение O = {0, 0, 0}
  • Va - эталон для измерения левого вращения при Vn

Угол должен быть измерен таким образом, чтобы, если плоскость была бы плоскостью XY, Va стояла бы за единичный вектор оси X.

Я предполагаю, что я должен выполнить какое-то преобразование координатного пространства, используя Va как ось X и поперечное произведение Vb и Vn как ось Y, а затем просто используя некоторый 2d-метод, подобный с atan2() или чем-то, Есть идеи? Формулы?

Ответ 1

Используйте кросс-произведение двух векторов, чтобы получить нормаль плоскости, образованной двумя векторами. Затем проверьте, что dotproduct между ним и исходной плоскостью нормальный, чтобы увидеть, находятся ли они в одном направлении.

angle = acos(dotProduct(Va.normalize(), Vb.normalize()));
cross = crossProduct(Va, Vb);
if (dotProduct(Vn, cross) < 0) { // Or > 0
  angle = -angle;
}

Ответ 2

Решение, которое я сейчас использую, похоже, отсутствует здесь. Предполагая, что нормаль плоскости нормирована (|Vn| == 1), подписанный угол просто:

atan2((Vb x Va) . Vn, Va . Vb)

который возвращает угол в диапазоне [-PI, + PI] (или независимо от возвращаемой реализации atan2).

. и x являются точками и поперечными произведениями соответственно.

Не требуется явное разветвление и вычисление длины деления/длины вектора. Используйте Va x Vb для вращения правой руки вместо левой

Объяснение, почему это работает: пусть альфа - прямой угол между векторами (от 0 ° до 180 °) и бета - угол, который мы ищем (от 0 ° до 360 °), с beta == alpha или beta == 360° - alpha

Va . Vb == |Va| * |Vb| * cos(alpha)    (by definition) 
        == |Va| * |Vb| * cos(beta)     (cos(alpha) == cos(-alpha) == cos(360° - alpha)


Va x Vb == |Va| * |Vb| * sin(alpha) * n1  
    (by definition; n1 is a unit vector perpendicular to Va and Vb with 
     orientation matching the right-hand rule)

Therefore (again assuming Vn is normalized):
   n1 . Vn == 1 when beta < 180
   n1 . Vn == -1 when beta > 180

==>  (Va x Vb) . Vn == |Va| * |Vb| * sin(beta)

Наконец

tan(beta) = sin(beta) / cos(beta) == ((Va x Vb) . Vn) / (Va . Vb)

Ответ 3

Вы можете сделать это в два этапа:

  • Определите угол между двумя векторами

    theta = acos (точечный продукт Va, Vb). Предполагая, что Va, Vb нормированы. Это даст минимальный угол между двумя векторами

  • Определите знак угла

    Найти вектор V3 = поперечное произведение Va, Vb. (порядок важен)

    Если (точечный продукт V3, Vn) отрицателен, theta отрицателен. В противном случае theta положительна.

Ответ 4

Вы можете получить угол под знаком, используя dot product. Чтобы получить знак угла, возьмите знак Vn * (Va x Vb). В частном случае плоскости XY это сводится к просто Va_x*Vb_y - Va_y*Vb_x.

Ответ 5

Переместите один вектор в другой и нормализуйте его, чтобы получить единичный вектор.

Синус угла между двумя векторами равно величине поперечного произведения, деленному на величины двух векторов:

http://mathworld.wolfram.com/CrossProduct.html

Ответ 6

Предположим, что Vx является осью x, учитывая нормаль Vn, вы можете получить ось y поперечным произведением, вы можете проецировать вектор Vb в Vx и Vy (через точечный продукт вы можете получить длину проекции Vb на Vx и Vy), учитывая координату (x, y) на плоскости, вы можете использовать atan2 (y, x), чтобы получить угол в диапазоне [-pi, + pi]

Ответ 7

Advanced Customer предоставил следующее решение (изначально редактирование вопроса):

SOLUTION:

sina = |Va x Vb| / ( |Va| * |Vb| )
cosa = (Va . Vb) / ( |Va| * |Vb| )

angle = atan2( sina, cosa )

sign = Vn . ( Va x Vb )
if(sign<0)
{
    angle=-angle
}

Ответ 8

Пусть theta - угол между векторами. Пусть C = Va - поперечное произведение Vb. Тогда

sin theta = length (C)/(длина (Va) * Длина (Vb))

Чтобы определить, является ли theta положительным или отрицательным, помните, что C перпендикулярно Va и Vb, указывающим в направлении, определяемом правым правилом . Так, в частности, C параллельна Vn. В вашем случае, если C указывает в том же направлении, что и Vn, то theta отрицательно, так как вам нужно левое вращение. Вероятно, самый простой вычислительный способ быстро проверить, что Vn и C указывают в том же направлении, просто взять свой точечный продукт; если он положителен, они указывают в одном направлении.

Все это следует из элементарных свойств cross product.

Ответ 9

Это код Matlab для вычисления подписанного угла между двумя векторами u, v либо в 2D, либо в 3D. Код является самоочевидным. Выражение знака таково, что положительный + 90 ° выводится между ix и iy ([1,0,0], [0,1,0]) или iy и iz ([0,1,0], [0, 0,1])

function thetaDEG = angDist2Vecs(u,v)

if length(u)==3
    %3D, can use cross to resolve sign
    uMod = sqrt(sum(u.^2));
    vMod = sqrt(sum(v.^2));
    uvPr = sum(u.*v);
    costheta = min(uvPr/uMod/vMod,1);

    thetaDEG = acos(costheta)*180/pi;

    %resolve sign
    cp=(cross(u,v));
    idxM=find(abs(cp)==max(abs(cp)));
    s=sign(cp(idxM(1)));
    if s < 0
        thetaDEG = -thetaDEG;
    end
elseif length(u)==2
    %2D use atan2
    thetaDEG = (atan2(v(2),v(1))-atan2(u(2),u(1)))*180/pi;
else
    error('u,v must be 2D or 3D vectors');
end