3

I'm trying to implement the following in Java.

Given a list of circles of different sizes (possibly) and positions, determine a large circle (position and size) which just exactly encloses all the circles.

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

Any ideas?

Tomas
  • 57,621
  • 49
  • 238
  • 373
CodeGuy
  • 28,427
  • 76
  • 200
  • 317
  • 1
    Unless it's homework, this really has nothing to do with Java. – Karl Aug 07 '11 at 22:17
  • Well... ok, a first and easy solution -- which is NOT OPTIMAL but hey, it's fun to start somewhere -- could be: Treat each circle as a square, get xL,xR,yT,yB... Get the bounding rectangle that contains them all. Draw a circle that contains that rectangle (circle centered on the rectangle, with diameter equal to the rectangle's diagonal). (But no, it's not much Java is it...) I'm keen to see what the optimal solution is. Still pondering... – david van brink Aug 07 '11 at 22:22
  • I'd cycle through the circles and see what the maximum top, left, right and bottom coordinates are. That would give you a rectangle to cover. Drawing a circle at the center of gravity of the rectangle with a radius that matches the distance to one of the corners should do the trick. There might be a flaw in this reasoning though. – James P. Aug 07 '11 at 22:31
  • This is an interesting problem, but it really has nothing to do with Java. – Perception Aug 07 '11 at 23:52
  • possible duplicate of [Combined area of overlapping circles](http://stackoverflow.com/questions/1667310/combined-area-of-overlapping-circles) – trashgod Aug 08 '11 at 00:25
  • @thrashgod, your link is to a geometry problem, the problem here is a much harder optimization problem. At least, I can't reduce it to a geometry problem. – toto2 Aug 08 '11 at 00:31
  • what does it mean "just exactly encloses"? Be more specific. Are you looking for minimal bounding circle, i.e. bounding circle with smallest diameter? – Tomas Aug 08 '11 at 09:12
  • 1
    This is definitely not Java-specific... unless what you want is for people to write your code for you. - It's not a duplicate @trashgod, just closely related. – Kheldar Aug 14 '11 at 11:38
  • One possible algo here http://www.skycoyote.com/circles/ – Dr. belisarius Sep 10 '12 at 14:17

11 Answers11

4

The miniball-of-balls problem has been studied in "The smallest enclosing ball of balls: combinatorial structure and algorithms", for example. One outcome of this research was that at least some algorithms for the miniball-of-points problem (like Welzl's algorithm) cannot easily be generalised from points to balls.

The above paper presents an O(n)-algorithm to compute the miniball of a set of balls (n being the number of input balls, i.e., circles in 2D). A C++ implementation thereof is available in the Computational Geometry Algorithms Library (CGAL). (You do not need to use all of CGAL; simply extract the required header and source files.)

Note: I am a co-author of the above paper and the author of the CGAL Min_sphere_of_spheres package.

Hbf
  • 3,074
  • 3
  • 23
  • 32
1

I have a roughly O(n4) true solution that I'm implementing for a product in JavaScript:

  • You need a function to determine whether a solution is valid: to be precise, a function that will check if all the circles lie within the proposed super-circle. This is fairly trivial: for every circle Ci, require that the distance from the centre of the super circle to the centre of Ci plus the radius of Ci is less than or equal to the radius of the super-circle.

  • Then, construct a super-circle out of every pair and every triple of circles.

    • For a pair, draw a line from the centre of Ci to the centre of Cj. Extend the line out on each end by the radius of the respective circle. The midpoint of the line is the centre of the super-circle, and its radius is half the length of the line.

    • For 3 circles, this is the Problem of Apollonius: http://mathworld.wolfram.com/ApolloniusProblem.html; noting that you need to get the correct signs to get one that will include all three circles.

  • The correct solution is the valid super-circle with the smallest radius.

Here's my code:

'use strict';

/**
 * Epsilon value for floating point equality.
 * @const
 */
var EPSILON = 1E-6;

/**
 * Calculates the minimum bounding circle for a set of circles.
 * O(n^4)
 *
 * @param {Array.<Object.<string, number>>} circles A list of 2+ circles.
 * @return {Object.<string, number>} {cx, cy, radius} of the circle.
 */
function minimumBoundingCircleForCircles(circles) {

    var areAllCirclesInOrOnCircle = function(circle) {
        for (var i = 0; i < circles.length; i++) {
            if (!isCircleInOrOnCircle(circles[i], circle)) return false;
        }
        return true;
    };

    // try every pair and triple
    var best = {radius: 9E9};

    for (var i = 0; i < circles.length; i++) {
        for (var j = i + 1; j < circles.length; j++) {
            var circle = circleFrom2Circles(circles[i], circles[j]);
            if (areAllCirclesInOrOnCircle(circle) &&
                circle.radius < best.radius) {
                best.cx = circle.cx; best.cy = circle.cy;
                best.radius = circle.radius;
            }

            for (var k = j + 1; k < circles.length; k++) {
                var signs = [-1, 1, 1, 1];
                circle = apollonius(circles[i], circles[j], circles[k],
                                    signs);
                if (areAllCirclesInOrOnCircle(circle) &&
                    circle.radius < best.radius) {
                    best.cx = circle.cx; best.cy = circle.cy;
                    best.radius = circle.radius;
                }
            }
        }
    }

    return best;
}


/**
 * Calculates a circle from 2 circles.
 *
 * @param {Object.<string, number>} circle1 The first circle.
 * @param {Object.<string, number>} circle2 The second circle.
 * @return {Object.<string, number>} cx, cy, radius of the circle.
 */
function circleFrom2Circles(circle1, circle2) {

    var angle = Math.atan2(circle1.cy - circle2.cy,
                           circle1.cx - circle2.cx);

    var lineBetweenExtrema = [[circle1.cx + circle1.radius * Math.cos(angle),
                               circle1.cy + circle1.radius * Math.sin(angle)],
                              [circle2.cx - circle2.radius * Math.cos(angle),
                               circle2.cy - circle2.radius * Math.sin(angle)]];

    var center = lineMidpoint(lineBetweenExtrema[0], lineBetweenExtrema[1]);
    return { cx: center[0],
             cy: center[1],
             radius: lineLength(lineBetweenExtrema[0], 
                                lineBetweenExtrema[1]) / 2
           };
}

/**
 * Solve the Problem of Apollonius: a circle tangent to all 3 circles.
 * http://mathworld.wolfram.com/ApolloniusProblem.html
 *
 * @param {Object.<string, number>} circle1 The first circle.
 * @param {Object.<string, number>} circle2 The second circle.
 * @param {Object.<string, number>} circle3 The third circle.
 * @param {Array.<number>} signs The array of signs to use.
 *                               [-1, 1, 1, 1] gives max circle.
 * @return {Object.<string, number>} The tangent circle.
 */
function apollonius(circle1, circle2, circle3, signs) {

    var sqr = function(x) { return x * x };

    var a1 = 2 * (circle1.cx - circle2.cx);
    var a2 = 2 * (circle1.cx - circle3.cx);
    var b1 = 2 * (circle1.cy - circle2.cy);
    var b2 = 2 * (circle1.cy - circle3.cy);
    var c1 = 2 * (signs[0] * circle1.radius + signs[1] * circle2.radius);
    var c2 = 2 * (signs[0] * circle1.radius + signs[2] * circle3.radius);
    var d1 = (sqr(circle1.cx) + sqr(circle1.cy) - sqr(circle1.radius)) -
        (sqr(circle2.cx) + sqr(circle2.cy) - sqr(circle2.radius));
    var d2 = (sqr(circle1.cx) + sqr(circle1.cy) - sqr(circle1.radius)) -
        (sqr(circle3.cx) + sqr(circle3.cy) - sqr(circle3.radius));

    // x = (p+q*r)/s; y = (t+u*r)/s

    var p = b2 * d1 - b1 * d2;
    var q = (- b2 * c1) + (b1 * c2);
    var s = a1 * b2 - b1 * a2;
    var t = - a2 * d1 + a1 * d2;
    var u = a2 * c1 - a1 * c2;

    // you are not expected to understand this.
    // It was generated using Mathematica's Solve function.
    var det = (2 * (-sqr(q) + sqr(s) - sqr(u)));
    var r = (1 / det) * 
        (2 * p * q + 2 * circle1.radius * sqr(s) + 2 * t * u -
         2 * q * s * circle1.cx - 2 * s * u * circle1.cy + signs[3] *
         Math.sqrt(sqr(-2 * p * q - 2 * circle1.radius * sqr(s) - 2 * t * u +
                       2 * q * s * circle1.cx + 2 * s * u * circle1.cy) - 
                   4 * (-sqr(q) + sqr(s) - sqr(u)) * 
                   (-sqr(p) + sqr(circle1.radius) * sqr(s) - sqr(t) +
                    2 * p * s * circle1.cx - sqr(s) * sqr(circle1.cx) + 
                    2 * s * t * circle1.cy - sqr(s) * sqr(circle1.cy))))

    //console.log(r);
    r = Math.abs(r);

    var x = (p + q * r) / s;

    var y = (t + u * r) / s;

    //console.log(x); console.log(y);
    return {cx: x, cy: y, radius: r};
}

/**
 * Is the circle inside/on another circle?
 *
 * @param {Object.<string, number>} innerCircle the inner circle.
 * @param {Object.<string, number>} outerCircle the outer circle.
 * @return {boolean} is the circle inside/on the circle?
 */
function isCircleInOrOnCircle(innerCircle, outerCircle) {
    return ((lineLength([innerCircle.cx, innerCircle.cy],
                        [outerCircle.cx, outerCircle.cy]) +
             innerCircle.radius - EPSILON) < outerCircle.radius);
}


/**
 * Calculates the length of a line.
 * @param {Array.<number>} pt1 The first pt, [x, y].
 * @param {Array.<number>} pt2 The second pt, [x, y].
 * @return {number} The length of the line.
 */
function lineLength(pt1, pt2) {
    return Math.sqrt(Math.pow(pt1[0] - pt2[0], 2) +
                     Math.pow(pt1[1] - pt2[1], 2));
}

/**
 * Calculates the midpoint of a line.
 * @param {Array.<number>} pt1 The first pt, [x, y].
 * @param {Array.<number>} pt2 The second pt, [x, y].
 * @return {Array.<number>} The midpoint of the line, [x, y].
 */
function lineMidpoint(pt1, pt2) {
    return [(pt1[0] + pt2[0]) / 2,
            (pt1[1] + pt2[1]) / 2];
}
dja
  • 1,453
  • 14
  • 20
0

The Wikipedia article Smallest circle problem describes a linear average time algorithm for the case where the sizes of the initial circles are equal. It looks straightforward to generalize it to differently-sized initial circles, though I'm not sure what happens to the complexity analysis then.

hmakholm left over Monica
  • 23,074
  • 3
  • 51
  • 73
  • 1
    I am not sure it is straightforward to generalize these algorithms that enclose points to algorithms that enclose disks of varying radii... – Joseph O'Rourke Jan 05 '13 at 02:03
  • I agree with @JosephO'Rourke that it's not completely evident how to generalise these algorithms to the case of different radii – at least not for me :) However, there is an algorithm and a C++ implementation in the [Computational Geometry Algorithms Library (CGAL)](http://www.cgal.org/Manual/latest/doc_html/cgal_manual/Bounding_volumes/Chapter_main.html). (You do not need to use all of CGAL; simply extract the required header and source files.) – Hbf Mar 25 '13 at 09:18
-1

You could find the max boundaries (xmin, xmax, ymin, ymax), then take max on both axis, then ask java to draw an ellipse in that square or take the center of it and the side as diameter.

No ?

Regards, Stéphane

Snicolas
  • 37,840
  • 15
  • 114
  • 173
  • (xmin - corresponding_radius, xmax + c_radius) (ymin/max +- c_radius) might be a good beginning, but not sufficient. Think of a 8x8 matrix, with circles of radius 1 at (0,-4), (0, 4), (-4,0), (4,0). A big circle around (0,0) with radius 5 would cover them all, but another circle at (4,4) with radius 1 would not change min/max (x/y), but the big circle wouldnt cover it. – user unknown Aug 07 '11 at 22:26
-1

Oops, the following does not work, as noted in comments:

Start by solving the problem for 3 circles. In that case the circumscribed circle will touch each of the three inner circles, and you can find its center as the intersection of two hyperbolas. (The locus of points that have a given difference in distance to two fixed points is a hyperbola). After a bit of algebra, that boils down to a quadratic equation.

Now add more inner circles by induction. At the beginning of each step you know the smallest circle that encompasses all of the old circles; it will touch three particular old "corner" circles. If the new circle is inside that one, there's nothing to do. If not, combine the new circle with all three ways to chose two of the old corner circles and compute the circumscribed circle for each of those triples. One of them should include the fourth one, which is now not a corner circle anymore.

Proceed until you have added all circles.

This gives a linear-time algorithm with bounded rounding errors (because each circumscribed circle is computed afresh from pristine input coordinates).

hmakholm left over Monica
  • 23,074
  • 3
  • 51
  • 73
  • I like this a lot. There's some (many?) cases where the best circle only touches two of the inner circles, though. Three in a row, or nearly in a row, for example. – david van brink Aug 08 '11 at 01:02
  • Hm, you're right. And in fact this spoils the entire scheme, because it is possible that after you add a new circle very far away, the new circumscribed circle touches the new one _and_ an inner circle that wasn't even at one of the corners before. Back to the drawing board... – hmakholm left over Monica Aug 08 '11 at 01:07
  • i wonder if you can keep the "best 3"... but at the end possibly reduce it to 2. shooting in the dark here. :) – david van brink Aug 08 '11 at 01:08
  • I'm afraid the entire "best 3" thing is unsalvageable. For an example, imagine that we have four radius-0 circles centered at A(0,1), B(1,0), C(10,0), D(0,10) and E(10,10). Then the circumscribed circle touches C, D and E. But if we add F(1000, 1000), then suddenly our corner points are A, B and F, with not a single one in common with the previous ones. – hmakholm left over Monica Aug 08 '11 at 01:23
-1

I think that this can be done in three steps:

  • First bounding circle c1:

    • The centre is determined by xc1 = (xmax + xmin) / 2 and yc1 = (ymax + ymin) / 2.
    • For each circle, calculate the distance of its centre to the centre of c1 plus its radius (I call this the over-distance). The maximum of these values is the radius of c1. The corresponding circle is a.
  • Second bounding circle c2: (In this step, you move the centre of c1 in direction of a as far as possible.)

    • For each circle except a, determine how much you have to move the centre of c1 in the direction of a so that the over-distance from there to this circle is the same as to a. The minimum of this determines the centre of c2. The corresponding circle is b. The over-distance to a and b (both are the same) is the radius of c2.
  • Third bounding circle c3: (In this step, you move the centre of c2 in the direction between a and b as far as possible.)

    • Determine the direction v in which you can move c2 such that the over-distance to a and b stays the same.
    • For each circle except a and b, determine how much you have to move the centre of c2 in direction v so that the over-distance from there to this circle is the same as to a and b. The minimum of this determines the centre of c3. The radius is the over-distance to the three circles found.

I believe that c3 is the solution (edit) a good first approximation. You can get better solutions by iteratively dropping the first circle and repeating the third step. If you arrive at a set of three circles that you have already seen, this should might be the final solution.

Svante
  • 50,694
  • 11
  • 78
  • 122
  • Well, "you believe" :) The problem is with the selection of circle a, which was based on some "rectangle logic" instead of on proper optimization algorithm... however, your algorithm might be a trivial approximation (NOT A SOLUTION) worth trying. – Tomas Aug 08 '11 at 10:03
  • NOPE!! Your edit is wrong! This way you still can't aspire for anything more than just an approximation. It is a complex problem (see my answer). There is 2D set of possible solutions but you are just searching the optimum on some i-think-quite-good zig-zag line. When designing the algorithm you should prove that your solution is THE ONE. – Tomas Aug 08 '11 at 16:26
  • @Tomas Telensky: What part of "might be" confuses you? – Svante Aug 08 '11 at 19:37
  • I'm not confused in any way, I just state that your algorithm is just an approximation, don't even think about the optimal solution to be reached this way. "might be" .... just by chance in a very special case. – Tomas Aug 08 '11 at 20:07
-1

My suggested algorithm is similar to that of Svante but with a few differences.

The idea is to first create a circle which trivially encompasses all subcircles and then shrink it like a bubble until it is pinned by 1,2, or 3 circles.

first approximation:

a circle with centre 0,0 and radius max(distance from 0,0 of subcircle + radius of subcircle)

if the subcircle which determines the radius of the circle encloses all other subcircles, then it is trivially the correct result, and can be returned

second approximation:

reduce the radius of the circle found in the first approximation while keeping it tangent to the subcircle which it is 'pinned' to, moving the centre towards the pinned subcircle, until it becomes tangent to another subcircle.

final result:

reduce the radius of the circle again, keeping it tangent to the two subcircles found above, until either another subcircle becomes tangent to the circle, or the centre of the circle lies on the line between the centres of the two subcircles it is pinned to. This should be the minimum because there is no way from here to reduce the radius of the circle without one of the subcircles 'breaking through'

The part of the algorithm I'm not sure about is the 'reduce the radius until another subcircle becomes tangent' part. Obviously a binary search can give a good enough approximation in a decent amount of time, but I suspect you can reduce it to an equation.

Rcxdude
  • 135
  • 3
  • again, this is just an approximation, see my comments in Svante's algorithm. You should first make some proof before you state that your result is "final". You just selected some solution ignoring the infinity (2D set) of other possible solutions. – Tomas Aug 09 '11 at 12:09
-2

With two circles it is easy. A line through both centres will hit the perimeter where an enclosing circle would contact them.

With more circles you would need to apply FEM (finite element analysis- http://en.wikipedia.org/wiki/Finite_element_method) on each perimeter point with the potential to be a point of contact with the outside circle. This rules out those segments which are facing other circles, for example. The computation is still rather large as you proceed to apply different radius's to your points until you find the smallest ones that intersects all the others at a common point- the centre of your enclosing cirlce.

John
  • 6,433
  • 7
  • 47
  • 82
  • As JRL said, a packing problem, you will need some sort of numerical analysis (NA) as I don't believe a formula exists to reduce the complexity. FEM may work best but is illustrative of the technique of NA. – John Aug 07 '11 at 22:28
  • I doodled a bit on paper and I also think NA is needed. Worse, you also have local minima on your minimization problem, so you'd need also some algorithm that first figures out some starting point that gets you to the global minimum. Good luck. (I'd like to see someone with a better solution.) – toto2 Aug 07 '11 at 22:50
  • I don't think finite element methods will do the job. They are for solving differential equations, which is not this case. – Tomas Aug 08 '11 at 09:51
  • @Tomas this is an optimization problem with constraints where you must find the position of the optimal circle as well as it's radius. You'll get some differential equations when you write this optimization problem. – toto2 Aug 09 '11 at 13:45
  • @toto yes, it is optimization problem with constraints, but it won't lead to differential equations. It is more like minimax problem, more like quadratic programming. – Tomas Aug 09 '11 at 14:13
  • @Tomas I would like to see your solution without differential equations. The position of the center of the optimal circle as well as its radius will have to be found from some continuous optimization method. I would start with a very large radius and shrink it; when the enclosing circles starts to hit the other circles, the center of the enclosing circle will have to move. – toto2 Aug 09 '11 at 20:52
  • @toto: If you think so, just put a solution with differential equations and we'll see... – Tomas Aug 09 '11 at 22:30
  • @Tomas I don't think FEM just for differential equations. It is used in structural analysis in civil engineering and I recall Young & Freedman use it in an early "University Physics" to calculate field distribution. – John Aug 11 '11 at 22:26
  • I think NA was actually called Numerical Methods, NM, but same thing. – John Aug 11 '11 at 22:27
-2

It is very difficult problem, I would just outline possible ways to it, you have to finish it yourself. I assume you want to find minimal bounding circle. Formalizing your problem - having xi, yi, ri for i = 1..N, you are looking for point [x, y] such that:

max(distance([x, y], [xi, yi]) + ri)

is minimal. This is a non-trivial minimax problem. First look at the simpler version of this problem Smallest circle problem, which is just for points:

max(distance([x, y], [xi, yi]))

So first try to improve the algorithms proposed in the above link to solve the more complex case. If this way is impassable, you might probably need to go for quadratic programming. Good luck...

Tomas
  • 57,621
  • 49
  • 238
  • 373
-2

I don't think its a packing problem per se. It sounds more like convex-hull. I think the question is:

You are given a set of circles on the plane. Find the center point and radius of the smallest circle for which every boundary point of each circle lies within or on the boundary of the containing circle.

For that, you can just run recursively: find the smallest circle containing the first two circles (and that circle's center lies on the line connecting the two centers, and its radius should be simple to determine as well), replace the first two circles with the new circle, and repeat.

The correctness of this algorithm belongs in mathematics.

Foo Bah
  • 25,660
  • 5
  • 55
  • 79
  • agree to first two paragraphs. The algorithm mentioned in 3rd paragraph will of course not work, because while encapsulating the circles you make a new boundary which was not present in the circles given (and make the final circle unnecessarily big). – Tomas Aug 09 '11 at 12:12
  • you can prove that no unnecessary points will be added. Not enough space to give a full proof; does SO even support tex? – Foo Bah Aug 09 '11 at 14:43
  • this is obviously not true, it can be seen immediatelly that by encapsulation you of course add new area which will need to be covered in the next step. Guys, you should make proof before you claim something to be the optimal solution. – Tomas Aug 09 '11 at 22:28
  • Try to devise a counter example. Just because you "add new area" doesnt mean that you arent adding points that must be covered. As an example, consider the circles with radius 1 centered at (0,0) and (5,0). Any circle containing both circles must also contain the box with corners (0,-1), (0,1), (5,1), (5,-1). – Foo Bah Aug 09 '11 at 22:40
  • Yees, but following your algorithm, you make encapsulating circle (2.5, 0, 2.5) which goes to another iteration. And this circle contains also e.g. point (2.5, 2.5), which is not contained within the two original circles, and need not necessarily be contained in the final circle. – Tomas Aug 09 '11 at 23:20
-2

I'd try to find the topmost western point, then the southern downmost point, then make a circle with those points as diameter. In order to find those points, I'd cycle through the center of the circles and their radius.

So it ends up in:

 initiate max object and min object to average center of circle and zero radius
 For every circle object
     calculate topmost western point of the circle
     check if further away than current point
     if further, calculate position of topmost western point required to pass over this new point and set as new min object
     calculate down most eastern point of the circle
     do the same as previous step, only for max object
 make a circle with center equals to middle of min-max line and radius equals to half min-max line length

If you are the bookworm type, good university libraries stock this: E.Welzl, Smallest Enclosing Disks (Balls and Ellipsoids), in H. Maurer (Ed.), New Results and New Trends in Computer Science, Lecture Notes in Computer Science, Vol. 555, Springer-Verlag, 359–37 (1991)

And if you want to read C++ code and adapt it to Java, http://miniball.sourceforge.net/. With circles, d=2, of course.

Kheldar
  • 5,361
  • 3
  • 34
  • 63
  • Instead of http://miniball.sourceforge.net/, please use the CGAL version, which is more up-to-date. (I am the author of http://miniball.sourceforge.net/). – Hbf Mar 25 '13 at 09:15