如何确保Math.acos获得[-1..1]范围内的值

时间:2019-04-04 07:22:42

标签: javascript math trigonometry

计算必须使用Math.acos的两个向量之间的角度( EDIT :由于存在Math.acos方式,因此不必使用Math.atan2) ,仅接受[-1..1]范围内的值。但是如果vector1.x==vector2.xvector1.y==vector2.y且由于JS的性质而导致0.1+0.2>0.3有时Math.acos得到>1的情况,并且返回{{ 1}}。

我在所有计算和NaN之前只进行一次if-check来解决它 还有if (v1.x==v2.x&&v1.y==v2.y)

我是这样做的(接受的答案中的 EDIT 具有更好的版本):

return 0

它可以工作,但是,如果有动态图形处理,是否有比60 fps时的中等复杂逻辑检查更好,更有效的方法呢?

2 个答案:

答案 0 :(得分:2)

请勿使用acos,而应使用atan2。为了使acos工作,必须对差异进行归一化,并以不同的角度处理pi> pi大小写。使用atan2,您只需提供y,x坐标的差,其余的就可以正确处理。

编辑

您想要矢量之间的角度,而不是差矢量的参数,我不好。我们只需要更改您处理计算错误的方式即可。带有一些伪代码:

function angle_between(ax, ay, bx, by) {
    var al = ax*ax+ay*ay;
    var bl = bx*bx+by*by;
    var dot = (ax*bx+ay*by)/Math.sqrt(al2*bl2);
    if (dot >= 1) return 0;
    if (dot <= -1) return Math.PI;
    return Math.acos(dot);
}

EDIT2

好的,让我们也看看atan2解决方案。正如@njuffa指出的那样,atan2仍然可以用于计算两个向量之间的角度。少一平方根,这很好。它还为我们提供了一个符号角,对于某些应用程序甚至更好。

function signed_angle_between(ax, ay, bx, by) {
    var dot = ax*bx + ay*by;
    var cross = ax*by - ay*bx;
    return Math.atan2(cross, dot);
}

console.log(signed_angle_between(3, 4, 30, 40));
console.log(signed_angle_between(2, 5, -50, 20));
console.log(signed_angle_between(2, 5, 50, -20));
console.log(signed_angle_between(1, 1, -1, -1));

答案 1 :(得分:0)

基于acos的计算不仅存在因舍入误差引起的伪NaN的问题,导致其自变量超过1,而且还存在数值问题,其结果接近0和π,导致结果不准确

一种避免这两个问题的高级方法是基于atan2:角(a,b)= atan2(| a×b |,a·b)。我不知道Javascript,但是希望以下实现此功能的ISO-C代码能将一对一的翻译成Javascript:

double angle (double ax, double ay, double bx, double by)
{
    double dot = ax * bx + ay * by;
    double norm_cross = fabs (ax * by - ay * bx);
    return atan2 (norm_cross, dot);
}

根据我的经验,使用atan2进行的计算应该与通过acos进行的计算具有大致相同的性能,但是当然是否取决于所用数学库的具体情况。