找到两点之间角度的最快方法

Posted

技术标签:

【中文标题】找到两点之间角度的最快方法【英文标题】:Fastest way to find the angle between two points 【发布时间】:2012-10-15 20:32:27 【问题描述】:

为了提高找到角度的正弦/余弦的速度,我建立了一个参考表,而不是即时计算它们。我也有同样的想法来找到从一个点到另一个点的角度。

我创建了一个包含 3600 个归一化向量的表(3600 / 10 = 十分之一度的精度)。每当我需要知道从一个点到下一个点的角度时,我都会查看表格以找到最佳匹配。但是,我担心这可能比使用 math.atan2() 慢。

这是我正在使用的代码:

创建向量表:

// vector to angle table
var vectorToAngleTable = new Array();
for (i = 0; i < 3600; i += 1) 
    vectorToAngleTable[i] = new Vector2();
    vectorToAngleTable[i] = RotatePoint(forwardVector, i / 10);

从两点求角度:

function NormalizeVector(vector) 
    var toReturn = vector;
    var dist = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
    toReturn.x /= dist.x;
    toReturn.y /= dist.y;
    return toReturn;


function PointDirection(position, target) 
    var vector = target;
    var toReturn = 0;
    var smallest = 1.0;
    vector.x -= position.x;
    vector.y -= position.y;
    vector = NormalizeVector(vector);
    for (i = 0; i < 3600; i += 1) 
        if (PointDistance(vectorToAngleTable[i], vector) < smallest) 
            smalllest = PointDistance(vectorToAngleTable[i], vector);
            toReturn = i;
        
    
    return toReturn;


function PointDistance(point1, point2) 
    return Math.sqrt(((point2.x - point1.x) * (point2.x - point1.x)) + ((point2.y - point1.y) * (point2.y - point1.y)));

如您所见,我关心的是它要遍历的所有代码行,以及它要遍历的表中有多少条目。我很想知道找到角度的最快方法,不管是什么方法。

【问题讨论】:

所属:codereview.stackexchange.com 使用内置函数几乎肯定更快(这是javascript)。特别是因为您现在的方法涉及取平方根,这在效率方面可能与使用反正切函数完全相同,因为 sqrt 可能是用大约 5 次牛顿近似迭代完成的,而 arctan 可能是用无穷级数的前几项。 toReturn.x /= dist.x; toReturn.y /= dist.y; 看起来不对。 dist 是一个标量,而不是另一个向量。 非常类似于***.com/questions/1427422/… 也类似于***.com/a/3441867/1615483 【参考方案1】:

作为angle(v1, v2) = acos( (v1x * v2x + v1y * v2y) / (sqrt(v1x^2+v1y^2) * sqrt(v2x^2+v2y^2)) ),我们知道v2 = [1, 0]

var v = x: 0, y: 1,
    angleRad = Math.acos( v.x / Math.sqrt(v.x*v.x + v.y*v.y) ),
    angleDeg = angleRad * 180 / Math.PI;

其中v 是向量[point2.x - point1.x , point2.y - point1.y]


编辑 - 我刚刚意识到你可能意味着将每个点视为一个向量,在这种情况下它会是

var v1 = x: 0, y: 1, v2 = x: 1, y: 0,
    angleRad = Math.acos( (v1.x * v2.x + v1.y * v2.y) / ( Math.sqrt(v1.x*v1.x + v1.y*v1.y) * Math.sqrt(v2.x*v2.x + v2.y*v2.y) ) ),
    angleDeg = angleRad * 180 / Math.PI;

其中v1 是向量[point1.x , point1.y]v2[point2.x , point2.y]


编辑 2 如果您多次使用向量长度,要加快速度,请将其保存为例如v.length = ... 这样你就可以得到它而无需再次重新计算。 如果您知道每个向量都需要多次计算其角度,请使用我编写的第一种方法并将其缓存,即v.angle = ...。你可以然后你可以做v2.angle - v1.angle来找到两者之间的角度,等等。 即有

function Vector(x, y)
    this.x = x;
    this.y = y;
    this.length = Math.sqrt(x*x + y*y);
    this.angle = Math.acos( x / this.length );


jsperf 的预计算和在 3601 项目的数组中查找与直接使用 acos

【讨论】:

哇,我印象深刻。区别是白天和黑夜。我没想到会有如此戏剧性的差异。无论如何,再次感谢你。 @dergenialeein 某人modified it 使用多项式逼近导致我here,这又是两倍快。请注意,这可能会有更大的错误。 @shhac:那是我,我只是将“acos 近似”放入我们光荣的搜索引擎霸主。老实说,很惊讶它的速度要快得多。 嗯,11 度的误差与慢 6 倍的误差,哪个更大?我猜如果游戏令牌计算其目标方向的频率足够高,它可以(在大多数情况下)补偿错误。谢谢 shhac 和 Phil H,这就是我正在寻找的东西。【参考方案2】:

这肯定会比调用atan2 更小,因为它是一个平方根,然后是对 3600 种可能性的线性搜索。相反,许多处理器直接实现 atan2——它是英特尔领域的 FPATAN。

【讨论】:

谢谢你,汤米。我知道 atan 是一种非常常见的计算,我觉得奇怪的是没有一个三角函数是直接通过处理器计算的。如果这是真的,那么我认为正弦和余弦也是如此?也就是说,仅使用它们比制作参考表更好。

以上是关于找到两点之间角度的最快方法的主要内容,如果未能解决你的问题,请参考以下文章

使用 OpenCV 查找图像中两点之间的角度

Java:以度为单位计算两点之间的角度

提高两点之间的几何角度

寻找两点之间的角度

查找 2 条折线之间的角度

千题案例TypeScript获取两点之间的距离 | 中点 | 补点 | 向量 | 角度