直接计算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的原因)的结果。 我发布的机会,其他人认为它有帮助。
从x
到y
的逆时针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)从cos
和sin
的几何定义开始。
如果通过“直接的方式”,你的意思是避免if
语句,那么我不认为有一个真正的一般解决scheme。
但是,如果你的特定问题会让angular度离散化失去一些精度,并且你可以放弃types转换的一些时间,那么你可以将[-pi,pi)允许的phiangular度范围映射到某个有符号整数types的允许范围。 那么你会得到免费的补充。 但是,我并没有在实践中使用这个技巧。 浮动到整数和整数到浮动转换的花费很可能会超过直接性的好处。 在angular度计算完成之后,最好将自己的优先级设置为编写自动插入或可并行编码。
另外,如果问题的详细信息使angular度方向的结果更可能,则可以使用编译器的内置函数将此信息提供给编译器,以便更高效地优化分支。 例如,在gcc的情况下,这是__builtin_expect
函数。 将它包装到likely
和unlikely
macros(比如在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; }