Search code examples
c#mathgeometryprecision

How can i make my math more stable for large circle radii


I have a function that checks if two circle annulus overlaps and works perfectly fine for small circles:

    public static bool Overlaps(in Circle circle1, in float annulus1, in Circle circle2, in float annulus2)
    {
        double h1 = annulus1 * 0.5f;
        double h2 = annulus2 * 0.5f;

        var innerRadius1 = circle1.Radius - h1;
        var outerRadius1 = circle1.Radius + h1;

        var innerRadius2 = circle2.Radius - h2;
        var outerRadius2 = circle2.Radius + h2;

        var d = math.distance(circle1.Origin, circle2.Origin);
        return d < outerRadius1 + outerRadius2 &&
               d > innerRadius1 - outerRadius2 &&
               d > innerRadius2 - outerRadius1;
    }

enter image description here

However the problem is for very large circles, it does not detect overlap for example:

enter image description here

This shows green suggesting there is no overlap. The input data is:

Circle 1:
    Origin: (2502,3.09)
    Radius: 2500
    Annulus: 1

Circle 2:
    Origin: (6.33,2.39)
    Radius: 2.43
    Annulus: 1

The reason I need to support very large circles is because i am actually using arcs, and i use a circle annulus overlap test to early exit from doing the more heavy computations for arc intersections calculations.

How can i make this more stable? I tried using double but that still failed to correctly detect overlap.


Solution

  • There are two extremes where the shapes don't overlap. When they are far apart, and when one shape is completely inside the other. You need to ignore where it is impossible for one shape to fit inside the other.

    d < outerRadius1 + outerRadius2 &&
    (d > innerRadius1 - outerRadius2 || innerRadius1 < outerRadius2) &&
    (d > innerRadius2 - outerRadius1 || innerRadius2 < outerRadius1)
    

    Still I think its easier to read & reason about the following;

    var max = outerRadius1 + outerRadius2;
    var min = 0f;
    
    if (min < innerRadius1 - outerRadius2)
        min = innerRadius1 - outerRadius2;
    if (min < innerRadius2 - outerRadius1)
        min = innerRadius2 - outerRadius1;
    
    return d >= min && d <= max;