【问题标题】:Fast circle collision detection快速圆碰撞检测
【发布时间】:2010-10-16 09:07:47
【问题描述】:

我正在尝试编写一个方法来计算两个圆是否重叠。我想出了以下内容,我只是想知道是否可以进一步优化它。

private static boolean isCollision(Point2D p1, float r1, Point2D p2, float r2)
{
    float a,dx, dy;
    a = (r1+r2) * (r1+r2);
    dx = (float) (p1.getX() - p2.getX());
    dy = (float) (p1.getY() - p2.getY());

    if (a > (dx*dx) + (dy*dy))
    {
        return true;
    }
    return false;
}

【问题讨论】:

  • 我不认为任何解决方案都能提供足够的结果,即 2 个中心之间的距离小于 1 但大于 0。

标签: java optimization collision-detection performance geometry


【解决方案1】:

重叠还是相交?

如果相交,不要忘记圆圈不相交的情况,因为它们在彼此内部。

如果重叠,我真的不知道您如何进一步优化;您正在将点距离与半径之和进行比较,使用距离平方来避免取平方根。好像没有多余的脂肪要修剪了。

【讨论】:

    【解决方案2】:

    嗯。就数学而言,这看起来相当不错。关于如何使 Java 方面更快、更简洁的一些小问题:

    • 如果您使用双精度而不是浮点数作为半径,则不必将双精度数向下转换为浮点数。
    • 如果您特别要求 Point2D.Double 参数,您可以使用它们的 x 和 y 公共字段,而不是使用 getter。
    • 另外,为什么if (foo) { return true; } else { return false; }? 就做return foo;

    一个改进的版本,然后:

    private static boolean isCollision(Point2D.Double p1, double r1, Point2D.Double p2, double r2)
    {
        final double a = r1 + r2;
        final double dx = p1.x - p2.x;
        final double dy = p1.y - p2.y;
        return a * a > (dx * dx + dy * dy);
    }
    

    (请注意,如果您的代码完全基于浮点数,您可以使用Point2D.Floatfloats 执行相同的操作。)

    【讨论】:

    • 如果边界矩形不重叠,您也可以考虑提前终止。这是否真的值得实施将部分取决于实际重叠的圆形和矩形的数量。
    • 我一直在考虑这个问题。我的直觉(待我有更多时间后验证)是,对于 CPU 而言,使用分支将比执行更多浮点乘法更耗时。
    • 虽然我知道不强制转换为浮点数会更快,但如果只使用浮点数而不是双精度数会更有效吗?
    • 可能,实际上。只是Java中几乎没有其他东西似乎使用浮点数。另一件值得测试的事情。
    • 我不确定使用 getter 和 setter 方法是否会降低性能。看到这个帖子:stackoverflow.com/questions/23931546/…
    【解决方案3】:

    它不会让你的代码更快,但我更喜欢:

    return a > (dx*dx + dy*dy);
    

    【讨论】:

      【解决方案4】:

      您真的需要满足任何可能的 Point2D 实现吗?如果你不需要,它会节省一个虚拟通话:

      private static boolean isCollisionFloat (Point2D.Float p1, float r1, Point2D.Float p2, float r2)
      {
          final float r = r1+r2;
          final float dx = p1.x - p2.x;
          final float dy = p1.y - p2.y;
      
          return (r*r) > (dx*dx) + (dy*dy);
      }
      
      测试 1000x1000 点: 什么都不做需要 6 毫秒 执行 isCollision 传递 Point2D.Float 耗时 128 ms 执行 isCollision 传递 Point2D.Double 耗时 127 毫秒 执行 isCollisionFloat 耗时 71 毫秒 执行 isCollisionDouble 耗时 72 毫秒

      如果可以,请选择其中之一,而不是同时满足两者。


      perf 问题的问题在于,您确实必须衡量效果,到那时有人发布了与不受支持的意见相同的答案。

      【讨论】:

      • 嗯,现在很好奇制作 r、dx 和 dy 最终是否会产生性能差异。肯定伤不起。 无耻地复制到自己的答案中
      • 可能不会,但我习惯于做任何不改变最终的东西。
      • 这本身就是一件非常好的事情。我最近一直在推动(有点疯狂)的观点,即 Java 中的默认值应该是最终的,如果你想要一个变量,你必须使用“var”关键字......
      【解决方案5】:

      您的算法可以通过计算每个圆的矩形边界并查看它们是否重叠来进一步优化。如果它们不重叠,则返回 false。这避免了那些矩形边界不重叠的圆的乘法(即,它们彼此不接近)。矩形边界计算的加法/减法比乘法便宜。

      这是 Java 2D 使用的模式。见Shape.getBounds()

      【讨论】:

      • 我认为边界计算会失去大部分的奖金。但是你为什么不尝试一下并发布结果呢?
      【解决方案6】:

      我不知道它是否与您的情况相关,但如果您想检查您的圈子与许多其他圈子(比如说数千个圈子)之间的重叠,您可以尝试将您的圈子组织成四叉树(请参阅http://en.wikipedia.org/wiki/Quadtree)并在四叉树中进行树查找(基于圆的边界矩形)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-10-02
        • 2017-08-13
        • 1970-01-01
        相关资源
        最近更新 更多