7

I am making a game in which the user player places circles on the screen. It is important that the circles never overlap, so I need to figure out the nearest possible free spot from the cursor. I have found circle packing algorithms, but they do not seem a fit for my problem. I have also solved a similar problem in the past for boxes (here), but with circles, I cannot seem to figure it out.

I figured out how I can find the nearest free position when it intersects with one circle, or even when two are involved. However, I cannot find a robust algorithm that can deal with complex cases that have any number of circles in any arrangement.

Precise description of problem: I have a 2D space with any number of non-intersecting circles, all with identical radii (though that may not matter). I want to find a position for the next circle that will make it not intersect with any other circle, and which center [x,y] is nearest to a specified location [x,y].

Suggestions of any kind appreciated (references, approaches, or (Java) libraries).

p.s. Bonus points if the solution includes making sure the circle stays within a specific bounding box (i.e. display).

My final solution: (based on David Wallace's suggestions)

  • Calculate the minimal distance between the centers of two circles (in my case, all circles are the same size, so always 2*radius)
  • Make a list of all circles that are closer to the mouse position than the minimum distance
  • If 0 overlaps: all good!
  • If 1 overlap: move the new circle's center to the minimum distance from the compared circle's center, along the vector that runs from compared circle's center to mouse position
  • If 2 overlap: find out where the two overlapping circles intersect. Place the new circle on the intersection closest to the mouse position. If this position still overlaps with any circle, move to the other intersection. If that one doesn't work, leave the new circle were it is.
  • If 3 overlap: same as in 2 overlap, just take the two circles closest to the new circle.

Note that this does not work perfectly, but good enough in my case, where a user is dragging the new circle on the screen. It works in most cases and in those it doesn't, usually when there are many circles very close together, the new circle simply stays in the last position (which was valid). The user can then decide to drag it a fit further and be more precise in where he wants the new circle to go.

user1118321
  • 25,567
  • 4
  • 55
  • 86
Walmink
  • 167
  • 9
  • 2
    I think you should just check positions in a radius of, say, 20 pixels from the point user clicks and if any of them suits, draw a circle, otherwise tell user it's not allowed to place a circle there. Guess you don't want the situation when user clicks a point on one side of the screen and the circle appears on the other side of it cause the only free space is there. – svz Oct 15 '13 at 07:30
  • svz, that method is valid, but not very user-friendly. Also, finding the nearest spot will help users place a circle **flush** against another circle, which would be hard if the users had to eye-ball this themselves. – Walmink Oct 15 '13 at 08:09
  • A second reason why the **nearest** location is important, is that I want to continuously run this operation with moving the target [x,y] (by dragging the mouse). I predict that a system the returns the exactly nearest position will give a nice smooth "animation", in that the circle will nicely move along the edges of other circles. Of course, when moving it over a group of circles, it may jump from one side to the other, but that's fine. – Walmink Oct 15 '13 at 08:22

3 Answers3

4

This isn't a complete answer, but you may be able to make it into one.

Suppose you've already placed circles of radii r1, r2, r3 ... rn with centres C1, C2, C3 ... Cn, and you're looking to place a new circle of radius rz, the new circle's centre will have to be outside all of a set of "enlarged" circles, centred at C1, C2, C3 ... Cn; with radii (r1+rz), (r2+rz), (r3+rz) ... (rn+rz). So if the cursor is at point P, then there are some cases to consider.

(1) If P is not in any of the enlarged circles, then the problem is solved.

(2) If P is in just one of the enlarged circles, then move outwards along a radius of that circle, until you either reach a point that's outside all of the enlarged circles, or until you reach another enlarged circle. The former case reduces to scenario (1); the latter reduces to scenario (2). Pick an arbitrary direction if P happens to be the centre of the circle.

(3) If P is in several of the circles, then find the directions from P to each centre of a circle that it's in. Find the pair of directions that have the widest interval between them, and bisect that angle, to work out which direction to head along. For example, if the directions to the centres of the circles are 30deg, 120deg and 330deg, then bisect the angle between 120deg and 330deg - then head in a direction of 225deg. Head in that direction until you reach the edge of a circle, then recalculate. Keep doing this until you get back to scenario (2).

The thing that I can't work out is what to do if you get stuck in scenario (3). Maybe only allow a certain number of steps, then exit. After all, it's possible that there's no suitable place to put the circle.

Dawood ibn Kareem
  • 77,785
  • 15
  • 98
  • 110
  • Thanks, some of this got me going in the right direction. I have included my final solution in the description above. – Walmink Oct 15 '13 at 21:57
2

To calculate the distance between a point and a circle is with the center, considering your Circle class is like this one:

public class Circle{
    int x;
    int y;
    int radius;
}

public interface CircleHelper{
    public int distanceBetweenCircleAndPoint(Circle c, Point p);
    public int distanceBetweenTwoCircles(Circle c1, Circle c2);
}

First of all, I would think about using Quadtrees and check if there is any quad without surrounding circles

A quadtree

The quadtree deep can be selected considering the radius of the circles.

so if you have a point in one of the quads, you would look to its surrounding quads to check if there is any circle there and move from the point in the direction of empty quads.

I hope you understand my approach

RamonBoza
  • 8,898
  • 6
  • 36
  • 48
  • Thank you for your suggestion. While the quad tree would help finding a near place, it will not find the **nearest** location (because of the quad tree's discrete nature). The good thing about finding the absolute nearest position is that it would behave more like a physics simulation, i.e. if I would be dragging the mouse, the active circle would 'roll around' other circles. A physics simulation would however not do, because it will most likely freak out if I try to position a circle with large overlap onto an immovable other. – Walmink Oct 15 '13 at 08:13
  • RamonBoza, thanks for the suggestion! I want to avoid cross-posting, but if it doesn't work out here, I surely will. – Walmink Oct 15 '13 at 08:25
0

Here is a solution that will work for varying radiuses, and can be simplified if all radiuses are equal, as in your case. We first transform the problem slightly. Instead of fitting a circle among other circles, we extend the radiuses of all other circles by the radius of our circle to place, and instead try to place a point outside of these extended circles. This is equivalent to the original problem. We proceed as follows:

  1. First a special case. If the point is outside of all circles, we have a trivial solution.
  2. Find all the circles the point is inside. Calculate the closest point on their circumference (just move out from the original point along the radius).
  3. Find all the intersection points between pairs of circles.
  4. Combine the sets of points from steps 2 and 3, and filter these by finding the ones that are not covered by any other circle.
  5. Pick the closest point from the remaining set. Done!

This seems to be O(n^3), so not terribly fast, but should be doable if your set is not too huge.

Dag Ågren
  • 1,064
  • 7
  • 18