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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14066933/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 17:56:47  来源:igfitidea点击:

Direct way of computing clockwise angle between 2 vectors

c++mathangle

提问by Mircea Ispas

I want to find out the clockwise angle between 2 vectors(2D, 3D).

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

The clasic way with the dot product gives me the inner angle(0-180 degrees) and I need to use some if statements to determine if the result is the angle I need or its complement.

点积的经典方法给了我内角(0-180 度),我需要使用一些 if 语句来确定结果是我需要的角度还是它的补角。

Do you know a direct way of computing clockwise angle?

你知道计算顺时针角度的直接方法吗?

回答by MvG

2D case

二维案例

Just like the dot productis proportional to the cosine of the angle, the determinantis proprortional to its sine. So you can compute the angle like this:

就像点积与角度的余弦成正比一样,行列式与其正弦成正比。所以你可以像这样计算角度:

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)

The orientation of this angle matches that of the coordinate system. In a left-handed coordinate system, i.e. xpointing right and ydown as is common for computer graphics, this will mean you get a positive sign for clockwise angles. If the orientation of the coordinate system is mathematical with yup, you get counter-clockwise angles as is the convention in mathematics. Changing the order of the inputs will change the sign, so if you are unhappy with the signs just swap the inputs.

该角度的方向与坐标系的方向相匹配。在左手坐标系中,即x指向右而y向下,这在计算机图形学中很常见,这意味着您将获得顺时针角度的正号。如果坐标系的方向是数学上的,y向上,则按照数学惯例,您会得到逆时针角度。改变输入的顺序会改变符号,所以如果你对符号不满意,只需交换输入。

3D case

3D案例

In 3D, two arbitrarily placed vectors define their own axis of rotation, perpendicular to both. That axis of rotation does not come with a fixed orientation, which means that you cannot uniquely fix the direction of the angle of rotation either. One common convention is to let angles be always positive, and to orient the axis in such a way that it fits a positive angle. In this case, the dot product of the normalized vectors is enough to compute angles.

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

Plane embedded in 3D

嵌入 3D 的平面

One special case is the case where your vectors are not placed arbitrarily, but lie within a plane with a known normal vector n. Then the axis of rotation will be in direction nas well, and the orientation of nwill fix an orientation for that axis. In this case, you can adapt the 2D computation above, including ninto the determinantto make its size 3×3.

一种特殊情况是您的向量不是任意放置的,而是位于具有已知法向量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)

One condition for this to work is that the normal vector nhas unit length. If not, you'll have to normalize it.

使其起作用的一个条件是法向量n具有单位长度。如果没有,则必须对其进行规范化。

As triple product

作为三重产品

This determinant could also be expressed as the triple product, as @Excrubulentpointed out in a suggested edit.

正如@Excrubulent在建议的编辑中指出的那样,这个决定因素也可以表示为三重积

det = n · (v1 × v2)

This might be easier to implement in some APIs, and gives a different perspective on what's going on here: The cross product is proportional to the sine of the angle, and will lie perpendicular to the plane, hence be a multiple of n. The dot product will therefore basically measure the length of that vector, but with the correct sign attached to it.

这在某些 API 中可能更容易实现,并且对这里发生的事情给出了不同的观点:叉积与角度的正弦成正比,并且垂直于平面,因此是n的倍数。因此,点积将基本上测量该向量的长度,但附加了正确的符号。

回答by kassak

To compute angle you just need to call atan2(v1.s_cross(v2), v1.dot(v2))for 2D case. Where s_crossis scalar analogue of cross production (signed area of parallelogram). For 2D case that would be wedge production. For 3D case you need to define clockwise rotation because from one side of plane clockwise is one direction, from other side of plane is another direction =)

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

Edit: this is counter clockwise angle, clockwise angle is just opposite

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

回答by sircolinton

This answer is the same as MvG's, but explains it differently (it's the result of my efforts in trying to understand why MvG's solution works). I'm posting it on the off chance that others find it helpful.

这个答案与 MvG 的相同,但解释不同(这是我努力理解 MvG 解决方案为何有效的结果)。我发布它的机会是其他人觉得它有帮助。

The anti-clockwise angle thetafrom xto y, with respect to the viewpoint of their given normal n(||n|| = 1), is given by

相对于给定法线( )的视点,thetax到的逆时针角度由下式给出yn||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) = anti-clockwise angle between x axis and the vector (cos(theta), sin(theta))

(4) = theta

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) = θ

where ||x||denotes the magnitude of x.

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

Step (1) follows by noting that

步骤 (1) 之后注意到

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

交叉(x,y)= ||x|| ||y|| 罪(θ)n,

and so

所以

dot(n, cross(x,y))

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

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

点(n,交叉(x,y))

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

= ||x|| ||y|| 罪(θ)点(n,n)

which equals

等于

||x|| ||y|| sin(theta)

||x|| ||y|| 罪(θ)

if ||n|| = 1.

如果||n|| = 1

Step (2) follows from the definition of atan2, noting that atan2(cy, cx) = atan2(y,x), where cis a scalar. Step (3) follows from the definition of atan2. Step (4) follows from the geometric definitions of cosand sin.

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

回答by Nickolay Olshevsky

Scalar (dot) product of two vectors lets you get the cosinus of the angle between them. To get the 'direction' of the angle, you should also calculate the cross product, it will let you check (via z coordinate) is angle is clockwise or not (i.e. should you extract it from 360 degrees or not).

两个向量的标量(点)积可让您获得它们之间角度的余弦。要获得角度的“方向”,您还应该计算叉积,它会让您检查(通过 z 坐标)角度是否为顺时针(即您是否应该从 360 度中提取它)。

回答by nichole

For a 2D method, you could use the law of cosines and the "direction" method.

对于 2D 方法,您可以使用余弦定律和“方向”方法。

To calculate the angle of segment P3:P1 sweeping clockwise to segment P3:P2.

计算线段 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

operations as suggestions above and only one more or so floating point operation.

操作如上建议,只有一个或多个浮点运算。

the methods it uses are:

它使用的方法是:

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

回答by Vadim Khotilovich

If by "direct way" you mean avoiding the ifstatement, then I don't think there is a really general solution.

如果“直接方式”是指避免使用该if语句,那么我认为没有真正通用的解决方案。

However, if your specific problem would allow loosing some precision in angle discretization and you are ok with loosing some time in type conversions, you can map the [-pi,pi) allowed range of phi angle onto the allowed range of some signed integer type. Then you would get the complementarity for free. However, I didn't really use this trick in practice. Most likely, the expense of float-to-integer and integer-to-float conversions would outweigh any benefit of the directness. It's better to set your priorities on writing autovectorizable or parallelizable code when this angle computation is done a lot.

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

Also, if your problem details are such that there is a definite more likely outcome for the angle direction, then you can use compilers' builtin functions to supply this information to the compiler, so it can optimize the branching more efficiently. E.g., in case of gcc, that's __builtin_expectfunction. It's somewhat more handy to use when you wrap it into such likelyand unlikelymacros (like in linux kernel):

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

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

回答by theodore panagos

A formula for clockwise angle,2D case, between 2 vectors, xa,ya and xb,yb.

顺时针角度的公式,二维情况,在 2 个向量 xa,ya 和 xb,yb 之间。

Angle(vec.a-vec,b)=pi()/2*((1+sign(ya))* (1-sign(xa^2))-(1+sign(yb))* (1-sign(xb^2)))

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

回答by Red

just copy & paste this.

只需复制并粘贴这个。

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

you're welcome ;-)

别客气 ;-)