直接计算2个向量之间的顺时针角度的方法

时间:2012-12-28 08:51:20

标签: c++ math angle

我想找出2个矢量(2D,3D)之间的顺时针角度。

使用点积的clasic方式给出了内角(0-180度),我需要使用一些if语句来确定结果是否是我需要的角度或其补码。

您是否知道计算顺时针角度的直接方法?

9 个答案:

答案 0 :(得分:143)

2D案例

就像dot product与角度的余弦成正比一样,determinant与其正弦成正比。所以你可以像这样计算角度:

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)

此角度的方向与坐标系的方向匹配。在left-handed coordinate system中,即 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 )调整为determinant,使其大小为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 具有单位长度。如果没有,你必须将其标准化。

作为三重产品

此决定因素也可以表示为建议编辑中指出的triple product @Excrubulent

det = n · (v1 × v2)

这在某些API中可能更容易实现,并且对这里发生的事情提供了不同的视角:交叉积与角度的正弦成正比,并且垂直于平面,因此是<的倍数EM>名词的。因此,点积基本上会测量该矢量的长度,但附加了正确的符号。

答案 1 :(得分:5)

要计算角度,您只需要为2D情况调用atan2(v1.s_cross(v2), v1.dot(v2))。 其中s_cross是交叉生产的标量模拟(平行四边形的符号区域)。 对于2D楔形生产的情况。 对于3D情况,您需要定义顺时针旋转,因为从平面的一侧顺时针是一个方向,从平面的另一侧是另一个方向=)

编辑:这是逆时针角度,顺时针角度恰好相反

答案 2 :(得分:3)

这个答案与MvG相同,但解释不同(这是我努力理解为什么MvG解决方案有效的结果)。我发布的是其他人认为有用的机会。

thetax的逆时针角度y,相对于给定法线n||n|| = 1)的观点,给出了由

  

atan2(dot(n,cross(x,y)),dot(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

其中||x||表示x的大小。

注意步骤(1)

  

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

等等

  

dot(n,cross(x,y))

     

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

     

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

等于

  

|| X || ||ÿ||罪(THETA)

如果||n|| = 1

步骤(2)遵循atan2的定义,注意atan2(cy, cx) = atan2(y,x),其中c是标量。步骤(3)遵循atan2的定义。步骤(4)遵循cossin的几何定义。

答案 3 :(得分:2)

两个向量的标量(点)乘积可以得到它们之间角度的余弦。 为了获得角度的“方向”,你还应该计算十字产品,它会让你检查(通过z坐标)角度是顺时针还是没有(即你应该从360度提取它)。

答案 4 :(得分:1)

对于2D方法,您可以使用法则 余弦和&#34;方向&#34;方法

计算段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;
}

答案 5 :(得分:0)

如果通过“直接方式”表示避免使用if语句,那么我认为没有一个非常普遍的解决方案。

但是,如果您的特定问题会导致角度离散化失去一些精度,并且您可以在类型转换中省略一些时间,则可以将[-pi,pi]允许的角度范围映射到某些允许的范围内有符号整数类型。那么你将获得免费的互补性。但是,我在实践中并没有真正使用这个技巧。最有可能的是,浮点到整数和整数到浮点转换的费用​​将超过直接性的任何好处。在进行大量角度计算时,最好设置编写可自动恢复或可并行化代码的优先级。

此外,如果您的问题详细信息使得角度方向的结果更加明确,那么您可以使用编译器的内置函数将此信息提供给编译器,以便更有效地优化分支。例如,在gcc的情况下,那是__builtin_expect函数。当你将它包装到这样的likelyunlikely宏(比如在linux内核中)时,使用它会更方便:

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

答案 6 :(得分:0)

一个在两个向量xa,ya和xb,yb之间的顺时针2D情况的公式。

角度(vec.a-vec,b)= pi()/ 2 *((1 + sign(ya))*(1-sign(xa ^ 2))-(1 + sign(yb))* (1-sign(xb ^ 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)))

答案 7 :(得分: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);

不客气;-)

答案 8 :(得分:0)

由于最简单和最优雅的解决方案之一隐藏在评论中,我认为将其作为单独的答案发布可能会很有用。 <div *ngIf="submitted && registerForm.controls.name.errors"> <small class="form-text text-muted danger" *ngIf="f.name.errors.required" >Please enter a name!</small> </div> 可能会导致非常小的角度不准确,因此通常首选 acos。对于 3D 情况:

atan2