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

我想找出2个vector(2D,3D)之间的顺时针angular度。

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

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

2D情况

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

 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中,两个任意放置的vector定义它们自己的旋转轴线,垂直于两者。 这个旋转轴没有固定的方向,这意味着你不能唯一地固定旋转angular度的方向。 一个常见的惯例是让angular度始终为正,并且以适合正angular度的方式定向轴。 在这种情况下,归一化vector的点积足以计算angular度。

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

embedded在3D中的平面

一个特例就是你的向量不是任意放置的情况,而是位于一个具有已知法线向量n的平面内。 然后,旋转轴也将在方向n上 ,而n的方向将固定该轴的方向。 在这种情况下,可以将上面的二维计算(包括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在一个build议的编辑中指出的那样。

 det = n · (v1 × v2) 

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

要计算angular度,您只需要在2D情况下调用atan2(v1.s_cross(v2), v1.dot(v2)) 。 其中s_cross是交叉生产的标量类比(平行四边形的签名区域)。 对于2D的情况下,将是楔形生产。 对于三维的情况下,你需要定义顺时针旋转,因为从平面的一侧顺时针是一个方向,从平面的另一侧是另一个方向=)

编辑:这是逆时针angular度,顺时针angular度正好相反

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

这个答案和MvG的答案是一样的,但是解释方式不同(这是我努力理解MvG解决scheme的原因)的结果。 我发布的机会,其他人认为它有帮助。

xy的逆时针angulartheta相对于给定法线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轴与vector(cos(theta),sin(theta))之间的逆时针angular度

(4)=θ

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

步骤(1)通过注意到

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

所以

dot(n,cross(x,y))

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

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

等于

|| X || ||ÿ|| SIN(THETA)

如果||n|| = 1 ||n|| = 1

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

如果通过“直接的方式”,你的意思是避免if语句,那么我不认为有一个真正的一般解决scheme。

但是,如果你的特定问题会让angular度离散化失去一些精度,并且你可以放弃types转换的一些时间,那么你可以将[-pi,pi)允许的phiangular度范围映射到某个有符号整数types的允许范围。 那么你会得到免费的补充。 但是,我并没有在实践中使用这个技巧。 浮动到整数和整数到浮动转换的花费很可能会超过直接性的好处。 在angular度计算完成之后,最好将自己的优先级设置为编写自动插入或可并行编码。

另外,如果问题的详细信息使angular度方向的结果更可能,则可以使用编译器的内置函数将此信息提供给编译器,以便更高效地优化分支。 例如,在gcc的情况下,这是__builtin_expect函数。 将它包装到likelyunlikelymacros(比如在Linux内核中)时,使用起来会更方便一些:

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

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

要计算P3段的angular度: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 

如上面的build议操作,只有一个浮点运算。

它使用的方法是:

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