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

Sta*_*lev 5 javascript math trigonometry

计算必须使用的两个向量之间的角度Math.acos编辑:事实证明不必使用Math.acos,因为有Math.atan2-way),它只接受 范围内的值[-1..1]。但是 ifvector1.x==vector2.xvector1.y==vector2.yA​​ND 由于 JS 的性质,会导致0.1+0.2>0.3有时Math.acos得到某些东西>1并且毫不奇怪地返回的情况NaN

我在所有计算之前用 if 检查来解决它,if (v1.x==v2.x&&v1.y==v2.y)我只是return 0 And if (v1.x==-v2.x&&v1.y==-v2.y) return Math.PI

我这样做了(编辑接受的答案有更好的版本):

function angle(origin, p1, p2, sign=false){
    if (p1.x==p2.x && p1.y==p2.y) return 0
    if (p1.x==-p2.x && p1.y==-p2.y) return Math.PI
    const a = {x: p1.x-origin.x, y: p1.y-origin.y}
    const b = {x: p2.x-origin.x, y: p2.y-origin.y}
    sign = sign && a.x*b.y < a.y*b.x ? -1 : 1
    return sign * Math.acos(
        (a.x*b.x+a.y*b.y)/(Math.sqrt(a.x**2+a.y**2)*Math.sqrt(b.x**2+b.y**2))
    )
}
Run Code Online (Sandbox Code Playgroud)

它可以工作,但是如果你做一些动态图形,是否有比以约 60fps 进行一堆中等复杂逻辑检查更好、更有效的方法呢?

Tam*_*dus 3

不要使用 acos,而是使用atan2 。为了使 acos 发挥作用,您必须对差异进行标准化,并以不同的方式处理 angle>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);
}
Run Code Online (Sandbox Code Playgroud)

编辑2

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

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);
}
Run Code Online (Sandbox Code Playgroud)