2009-03-30 5 views
14

Я пытаюсь написать метод, который будет вычислять, если два круга перекрываются. Я придумал следующее, и мне просто интересно узнать, есть ли в любом случае его можно оптимизировать дальше.Быстрое обнаружение столкновения круга

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; 
} 
+1

Я не думаю, что любое из решений обеспечит адекватный результат, когда расстояние между двумя центрами меньше единицы, но больше нуля. – 2009-12-08 22:05:39

ответ

21

Хм. Это выглядит довольно хорошо, насколько математика идет. Некоторые мелкие точки, как сделать сторону Java его быстрее и terser:

  • Если вы использовали двойники вместо поплавков для радиусов, то не было бы вниз отливки двойники к поплавкам.
  • Если вы задаете параметры Point2D.Double, вы можете использовать свои общедоступные поля x и y вместо использования геттеров.
  • Кроме того, почему 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.Float и float с)

+0

Вы также можете рассмотреть возможность раннего завершения, если ограничивающие прямоугольники не перекрываются. Независимо от того, действительно ли это будет реализовано, частично будет зависеть от того, сколько кругов и прямоугольников фактически перекрывается. –

+0

Я об этом думал. Мое чувство кишки (для проверки, когда у меня есть немного больше времени), заключается в том, что наличие филиалов будет более трудоемким для процессора, чем выполнение нескольких операций с плавающей запятой. – Zarkonnen

+0

Хотя я понимаю, что было бы быстрее не бросать в поплавок, было бы более эффективно, если бы вы просто использовали поплавки вместо двухместных? –

9

Перекрытие или пересечение?

Если пересекаться, не забывайте о случае, когда круги не пересекаются, потому что они находятся внутри друг друга.

Если это перекрытие, я не вижу, как вы могли бы оптимизировать дальше; вы сравниваете точки расстояния до суммы радиусов, используя квадрат расстояния, чтобы избежать квадратного корня. Кажется, нет жира, оставшегося на обрезке.

1

Это Безразлично не делайте свой код быстрее, но я бы предпочел:

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

Вам действительно нужно обслуживать любые возможные версии 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); 
} 
 
testing 1000x1000 points: 
Doing nothing took 6 ms 
Doing isCollision passing Point2D.Float took 128 ms 
Doing isCollision passing Point2D.Double took 127 ms 
Doing isCollisionFloat took 71 ms 
Doing isCollisionDouble took 72 ms 

Если вы можете выбрать одну или другую сторону, а не питание для обоих.


Проблема перфорация вопросов в том, что вы действительно должны измерить эффекты, к тому времени кто-то разместил один и тот же ответ, как неподдерживаемый мнение.

+0

Хмм, теперь * Мне очень любопытно, делает ли make r, dx и dy final разница в производительности. Это, конечно, не может повредить. * копирует бесстыдно в собственный ответ * – Zarkonnen

+0

Наверное, нет, но у меня есть привычка делать что-либо, что не меняется окончательно. –

+0

И это само по себе очень хорошо. Недавно я подталкивал (слегка сумасшедшую) точку зрения, что значение по умолчанию в Java должно быть окончательным, и вам нужно использовать ключевое слово «var», если вы хотите переменную ... – Zarkonnen

2

Ваш алгоритм может быть дополнительно оптимизирован путем вычисления прямоугольных границ каждого круга и просмотра, если они перекрываются. Если они не перекрываются, просто верните false. Это позволяет избежать умножения для тех кругов, у которых прямоугольные границы не перекрываются (т. Е. Они не близки друг к другу). Сложение/вычитание для вычисления прямоугольной границы дешевле, чем умножение.

Это шаблон, который использует Java 2D. См. Shape.getBounds()

+2

Я бы подумал, что вычисление границ потеряет большинство выигрышей. Но почему бы вам не попробовать и опубликовать результаты? –

3

Я не знаю, соответствует ли это вашему делу, но если вы хотите проверить наложение между кружком и многими другими кругами (скажем, тысячи кругов), вы можете попытаться организовать круги в квадрате -trees (см. http://en.wikipedia.org/wiki/Quadtree) и выполните поиск по дереву (на основе ограничивающего прямоугольника вашего круга) в квадратном дереве.

Смежные вопросы