views:

732

answers:

8

What is the best way to check collision of huge number of circles?
It's very easy to detect collision between two circles, but if we check every combination then it is O(n2) which definitely not an optimal solution.

We can assume that circle object has following properties:

  • Coordinates
  • Radius
  • Velocity
  • Direction

Velocity is constant, but direction can change.

I've come up with two solutions, but maybe there are some better solutions.

Solution 1
Divide whole space into overlapping squares and check for collision only with circles that are in the same square. Squares need to overlap so there won't be a problem when a circle moves from one square to another.

Solution 2
At the beginning distances between every pair of circles need to be calculated.
If the distance is small then these pair is stored in some list, and we need to check for collision in every update.
If the distance is big then we store after which update there can be a collision (it can be calculated because we know the distance and velocitites). It needs to be stored in some kind of priority queue. After previously calculated number of updates distance needs to be checked again and then we do the same procedure - put it on the list or again in the priority queue.

Answers to Mark Byers questions

  1. Is it for a game?
    It's for simulation, but it can be treated also as a game
  2. Do you want to recalculate the new position every n milliseconds, and also check for collisions at this time?
    Yes, time between update is constant.
  3. Do you want to find the time at which the first/every collision occurs?
    No, I want to find every collision and do 'something' when it occures.
  4. How important is accuracy?
    It depends on what do you mean by accuracy. I need to detect all collisions.
  5. Is it a big problem if very small fast moving circles can pass through each other occasionally?
    It can be assumed that speed is so small that it won't happen.
+13  A: 

There are "spatial index" data-structures for storing your circles for quick comparison later; Quadtree, r-tree and kd-tree are examples.

Solution 1 seems to be a spatial index, and solution 2 would benefit from a spatial index every time you recalculate your pairs.

To complicate matters, your objects are moving - they have velocity.

It is normal to use spatial indexes for objects in games and simulations, but mostly for stationary objects, and typically objects that don't react to a collision by moving.

It is normal in games and such that you compute everything at set time intervals (discrete), so it might be that two objects pass through each other but you fail to notice because they moved so fast. Many games actually don't even evaluate collisions in strict chronological order. They have a spatial index for stationary objects e.g. walls, and lists for all the moving objects that they check exhaustively (although with relaxed discrete checks as I outlined).

Accurate continuous collision detection and where the objects react to collisions in simulations is usually much more demanding.

The pairs approach you outlined sounds promising. You might keep the pairs sorted by next collision, and reinsert them when they have collided in the appropriate new positions. You only have to sort the new generated collision list (O(n lg n)) for the two objects and then to merge two lists (the new collisions for each object, and the existing list of collisions; inserting the new collisions, removing those stale collisions that listed the two objects that collided) which is O(n).

Another solution to this is to adapt your spatial index to store the objects not strictly in one sector but in each that it has passed through since the last calculation, and do things discretely. This means storing fast moving objects in your spatial structure, and you'd need to optimise it for this case.

Remember that linked lists or lists of pointers are very bad for caching on modern processors. I'd advocate that you store copies of your circles - their important properties for collision detection at any rate - in an array (sequential memory) in each sector of any spatial index, or in the pairs you outlined above.

As Mark says in the comments, it could be quite simple to parallelise the calculations.

Will
+1 Nice answer, I din't know about some of these data structures. I'll wait with the acceptance, maybe there will be some other answers. Thanks also for the part about trashing, it'll be also useful.
Tomek Tarczynski
I suggest that you also give some thought to how you might parallelise this. YOu should be able, quite easily, to get good speedups using all the cores on a quad-core, say.
High Performance Mark
@High Performance Mark: good point
Will
+2  A: 

Sub-divide your space up into regions and maintain a list of which circles are centred in each region.

Even if you use a very simple scheme, such as placing all the circles in a list, sorted by centre.x, then you can speed things up massively. To test a given circle, you only need to test it against the circles on either side of it in the list, going out until you reach one that has an x coordinate more than radius away.

rikh
+2  A: 

A suggestion - I am no game developer

Why not precalculate when the collisions are going to occur

as you specify

We can assume that circle object has following properties:

-Coordinates

-Radius

-Velocity

-Direction

Velocity is constant, but direction can change.

Then as the direction of one object changes, recalculate those pairs that are affected. This method is effective if directions do not change too frequently.

MaLio
But it's O(n^2) because You don't know between which pair of circles there will be a collision.
Tomek Tarczynski
The initial calculation might be O(n^2) but the update becomes simpler because you only recalculate when a circle changes dir. There also might be optimizations available for tracking which circles are affected by a direction change, i.e. only check the ones towards the direction change, or within some range depending on the scale of the change.
Kelly French
This is more or less the same what solution2 which I proposed.
Tomek Tarczynski
Every time a circle changes direction, you need to recheck it against every other circle. If collisions are frequent, this blows up in your face.
Alan
+2  A: 

You could make a 2D version of a "sphere tree" which is a special (and really easy to implement) case of the "spatial index" that Will suggested. The idea is to "combine" circles into a "containing" circle until you've got a single circle that "contains" the "huge number of circles".

Just to indicate the simplicity of computing a "containing circle" (top-of-my-head): 1) Add the center-locations of the two circles (as vectors) and scale by 1/2, thats the center of the containing circle 2) Subtract the center locations of the two circles (as vectors), add the radii and scale by 1/2, thats the radius of the containing circle

S.C. Madsen
+2  A: 

one possible technique is to use the Delaunay triangulation on the center of your circles.

consider the center of each circle and apply the delaunay triangulation. this will tesselate your surface into triangles. this allows you to build a graph where each node stores the center of a triangle, and each edge connects to the center of a neighbour circle. the tesselation operated above will limit the number of neighbours to a reasonable value (6 neighbours on average)

now, when a circle moves, you have a limited set of circles to consider for collision. you then have to apply the tesselation again to the set of circles which are impacted by the move, but this operation involves only a very small subset of circles (the neighbours of the moving circle, and some neighbours of the neighbours)

the critical part is the first tesselation, which will take some time to perform, later tesselations are not a problem. and of course you need an efficient implementation of a graph in term of time and space...

Adrien Plisson
Just to add to this: Delaunay triangulation takes `O(n log n)` so even if you do this every iteration instead of updating the triangulation somehow, it's a win over the naive `O(n^2)` algorithm.
Rex Kerr
@rex kerr: the problem is the amount of O in each case. the Delaunay triangulation involves operations which are far more complex than the naïve pairwise comparison. the advantage of the delaunay triangulation is that subsequent tesselation can be performed on a small subset of all the points.
Adrien Plisson
@Adrien: I realize that, but for large problems (which this presumably is, or else the poster wouldn't care about `n^2`) the increase in complexity is nowhere near as expensive as `n/log n`. Local updates, when possible, are better still, of course.
Rex Kerr
+1, Delaunay triangulation is really nice solution, but I need to think a little bit how to update graph in O(n) time - I think it's possible.
Tomek Tarczynski
@tomek tarczynski: you did not specify which is the target language. you can have a look at GTS (http://gts.sourceforge.net/) which is a C library implementing a delaunay triangulation. i have also found some implementations in Java (http://www.cs.cornell.edu/home/chew/Delaunay.html).
Adrien Plisson
+1  A: 

What answer is most efficient will depend somewhat on the density of circles. If the density is low, then placing placing a low-resolution grid over the map and marking those grid elements that contain a circle will likely be the most efficient. This will take approximately O(N*m*k) per update, where N is the total number of circles, m is the average number of circles per grid point, and k is the average number of grid points covered by one circle. If one circle moves more than one grid point per turn, then you have to modify m to include the number of grid points swept.

On the other hand, if the density is extremely high, you're best off trying a graph-walking approach. Let each circle contain all neighbors within a distance R (R > r_i for every circle radius r_i). Then, if you move, you query all the circles in the "forward" direction for neighbors they have and grab any that will be within D; then you forget all the ones in the backward direction that are now farther than D. Now a complete update will take O(N*n^2) where n is the average number of circles within a radius R. For something like a closely-spaced hexagonal lattice, this will give you much better results than the grid method above.

Rex Kerr
+1  A: 

As Will mentioned in his answer, spacial partition trees are the common solution to this problem. Those algorithms sometimes take some tweaking to handle moving objects efficiently though. You'll want to use a loose bucket-fitting rule so that most steps of movement don't require an object to change buckets.

I've seen your "solution 1" used for this problem before and referred to as a "collision hash". It can work well if the space you're dealing with is small enough to be manageable and you expect your objects to be at least vaguely close to uniformly distributed. If your objects may be clustered, then it's obvious how that causes a problem. Using a hybrid approach of some type of a partition tree inside each hash-box can help with this and can convert a pure tree approach into something that's easier to scale concurrently.

Overlapping regions is one way to deal with objects that straddle the boundaries of tree buckets or hash boxes. A more common solution is to test any object that crosses the edge against all objects in the neighboring box, or to insert the object into both boxes (though that requires some extra handling to avoid breaking traversals).

Alan
+6  A: 

I assume you are doing simple hard-sphere molecular dynamic simulation, right? I came accros the same problem many times in Monte Carlo and molecular dynamic simulations. Both of your solutions are very often mentioned in literature about simulations. Personaly I prefer solution 1, but slightly modified.

Solution 1
Divide your space into rectangular cells that don't overlap. So when you check one circle for collision you look for all circles inside a cell that your first circle is, and look X cells in each direction around. I've tried many values of X and found that X=1 is the fastest solution. So you have to divide space into cells size in each direction equal to:

Divisor = SimulationBoxSize / MaximumCircleDiameter;
CellSize = SimulationBoxSize / Divisor;

Divisor should be bigger than 3, otherwise it will cause errors (if it is too small, you should enlarge your simulation box).
Then your algorithm will look like this:

  1. Put all circles inside the box
  2. Create cell structure and store indexes or pointers to circles inside a cell (on array or on a list)
  3. Make a step in time (move everything) and update circles positions inside on cells
  4. Look around every circle for collision. You should check one cell around in every direction
  5. If there is a collision - do something
  6. Go to 3.

If you will write it correctly then you would have something about O(N) complexity, because maximum number of circles inside 9 cells (in 2D) or 27 cells (in 3D) is constant for any total number of circles.

Solution 2
Ususaly this is done like this:

  1. For each circle create a list of circles that are in distance R < R_max, calculate time after which we should update lists (something about T_update = R_max / V_max; where V_max is maximum current velocity)
  2. Make a step in time
  3. Check distance of each circle with circles on its list
  4. If there is a collision - do something
  5. If current time is bigger then T_update, go to 1.
  6. Else go to 2.

This solution with lists is very often improved by adding another list with R_max_2 > R_max and with its own T_2 expiration time. In this solution this second list is used to update the first list. Of course after T_2 you have to update all lists which is O(N^2). Also be carefull with this T and T_2 times, because if collision can change velocity then those times would change. Also if you introduce some foreces to your system, then it will also cause velocity change.

Solution 1+2 You can use lists for collision detection and cells for updating lists. In one book it was written that this is the best solution, but I think that if you create small cells (like in my example) then solution 1 is better. But it is my opinion.

Other stuff
You can also do other things to improve speed of simulation:

  1. When you calculate distance r = sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) + ...) you don't have to do square root operation. You can compare r^2 to some value - it's ok. Also you don't have to do all (x1-x2)*(x1-x2) operations (I mean, for all dimentions), because if x*x is bigger than some r_collision^2 then all other y*y and so on, summed up, would be bigger.
  2. Molecular dynamics method is very easy to parallelise. You can do it with threads or even on GPU. You can calculate each distance in different thread. On GPU you can easly create thousends of threads almost costless.

For hard-spheres there is also effective algorithm that doesn't do steps in time, but instead it looks for nearest collision in time and jumps to this time and updates all positions. It can be good for not dense systems where collisions are not very probable.

klew
+1, Actually I'm not dealing with molecular dynamics but with crowd dynamics, but surprisingly they have a lot in common.
Tomek Tarczynski