【问题标题】:How to efficiently calculate the angle between two points?如何有效地计算两点之间的角度?
【发布时间】:2012-06-10 18:08:09
【问题描述】:

我正在尝试优化实验中的模拟功能,以便一次运行更多的人工大脑控制代理。我分析了我的代码,发现我的代码现在的最大瓶颈是计算每个代理到每个代理的相对角度,即 O(n2),减去我所做的一些小优化.这是我用于计算角度的当前代码 sn-p:

[C++]
double calcAngle(double fromX, double fromY, double fromAngle, double toX, double toY)
{
    double d = 0.0;
    double Ux = 0.0, Uy = 0.0, Vx = 0.0, Vy = 0.0;

    d = sqrt( calcDistanceSquared(fromX, fromY, toX, toY) );

    Ux = (toX - fromX) / d;

    Uy = (toY - fromY) / d;

    Vx = cos(fromAngle * (cPI / 180.0));
    Vy = sin(fromAngle * (cPI / 180.0));

    return atan2(((Ux * Vy) - (Uy * Vx)), ((Ux * Vx) + (Uy * Vy))) * 180.0 / cPI;
}

我有两个二维点 (x1, y1) 和 (x2, y2 ) 和“从”点的朝向 (xa)。我想计算代理 x 需要转向(相对于其当前面向)面对代理 y 的角度。

根据分析器,最昂贵的部分是 atan2。我已经用谷歌搜索了几个小时,上面的解决方案是我能找到的最好的解决方案。有谁知道计算两点之间角度的更有效方法?我愿意为速度牺牲一点精度(+/- 1-2 度),如果这会影响任何事情的话。

【问题讨论】:

  • 不,抱歉。浮点非线性数学很昂贵。
  • “我的代码现在最大的瓶颈是计算每个代理到每个代理的相对角度”您是否考虑过...不这样做?首先,向量数学通常不需要角度;它只使用向量。您在需要特定角度的地方使用了哪些功能?其次,为什么你需要为每一对代理计算这个?你不是只需要为彼此亲近的人计算它吗?处理对附近实体进行分组的技术有很多。
  • sin andere cos 的查找表怎么样?你考虑过吗??
  • @Nicol: 我正在为每个代理模拟 180 度的正面视网膜,所以我需要这个角度,以便我知道将视网膜的哪个部分标记为“有另一个代理”在里面。”我已经预先计算了距离并过滤掉了超出代理可能视野距离的代理。我还考虑过在决定计算相对角度之前以某种方式确定代理 y 是否在代理 x 前面,但我发现这是一个难题,而不会产生类似的直接计算角度的成本。
  • @Matthias:我怀疑这会更快(假设我们在这里谈论的是现代桌面/服务器平台)。

标签: c++ math optimization game-physics


【解决方案1】:

正如 cmets 中提到的,可能有一些高级方法可以减少计算负载。

但是对于手头的问题,您可以使用dot-product relationship

theta = acos ( a . b / ||a|| ||b|| )

其中ab 是您的向量,. 表示“点积”,|| || 表示“向量幅度”。

基本上,这会将您的 {sqrt, cos, sin, atan2} 替换为 {sqrt, acos}。

我还建议在所有内部计算中坚持使用弧度,仅在人类可读的 I/O 中转换角度。

【讨论】:

  • 是否有稳定的 C++ 库可以自动处理这种游戏逻辑,例如 XNA Game Studio 的 C# 库?
  • @RandyOlson:恐怕这超出了我的专业领域。
  • 如果您知道如何计算点积和量级,为什么还需要一个库?它几乎不值得一个图书馆。
  • 游戏机制不仅仅是点积和向量的大小。
  • 你不需要sqrt 我认为:(a . b / ||a|| ||b||)² = cosθ² =(1+cos2θ)/2
【解决方案2】:

您的评论说明了很多:“我正在为每个代理模拟 180 度的正面视网膜,所以我需要角度”。不,你没有。您只需要知道位置矢量和视觉矢量之间的角度是大于还是小于 90 度。

非常简单:如果 A 和 B 之间的角度小于 90 度,点积 A·B 为 >0;如果角度正好是 90 度,则为 0,如果角度大于 90 度,则

【讨论】:

  • 我最终需要精确的(ish)角度来知道将视网膜的哪个部分标记为“其中有药剂”,但这是进一步减少次数的好主意该函数被调用。如果我有一个 90 度的额叶视网膜怎么办?谢谢!
  • 在这种情况下,请参阅 Oli 的回答(以及我的评论) - 您需要一个 acos,其余的只是 +-*/
  • 另外,您可能不需要角度。使用投影在视网膜平面上的相对矢量通常更容易。公式:V_projected = V - Normal*(V·Normal)(法线是垂直于平面的单位向量)。
【解决方案3】:

我认为这更像是一个数学问题:

试试

abs(arctan((y1-yfrom)/(x1-xfrom)) - arctan(1/((y2-yfrom2)/(x2-xfrom2))))

【讨论】:

  • 请注意,结果将以弧度为单位,您必须将其除以 pi 并乘以 180 以获得度数
  • 不是 fromAngle 一个角度,而 x1, x2, y1,y2 是坐标?不知道你在这里减去了什么。
  • xfrom 和 yfrom 都是直线起点的坐标... xfrom 将等价于他的 "xa": "face of the "from" point (xa)"
【解决方案4】:

使用这两个向量的dot product,最坏的情况是你需要做一个反余弦:

A = 面向方向。 B = 代理 Y 与代理 X 的方向

计算点是简单的乘法和加法。从那里你有角度的余弦。

【讨论】:

    【解决方案5】:

    对于初学者,您应该意识到有一些简化可以稍微减少计算:

    1. 你不需要计算一个代理到它自己的角度,
    2. 如果您知道从代理 i 到代理 j 的角度,那么您已经知道从代理 j 到代理 i 的角度。

    我要问:“代理i转向面对代理j”是什么意思?如果两个表面彼此正对,您是否需要进行计算?你对“直视对方”有什么容忍度?

    如果您不再专注于数学并更全面地描述问题,建议您做什么会更容易。

    【讨论】:

    • 当然已经在做 1 了。 :-) 不幸的是,2 并没有消除 atan2,因为 atan2 是相对于“来自”代理的面完成的。
    • 我正在为每个代理模拟一个 180 度的正面视网膜,所以我需要这个角度,以便我知道将视网膜的哪个部分标记为“其中有另一个代理”。我已经预先计算了距离并过滤掉了超出代理可能视野距离的代理。在决定计算相对角度之前,我还考虑过以某种方式确定代理 y 是否在代理 x 前面,但我发现这是一个难题,而不会产生直接计算角度的类似成本。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多