1
votes

I would like to do a collision detection between circle and section of a circular ring. The circle is defined by it's position position and it's radius. The other object is defined by inner and outer radius and then a startPoint and endPoint both [x, y] points.

In the examples below, this is the circle and other is the ring section.

First I just check if it's colliding with the full ring. This works without a problem.

float mag = this.position.Magnitude();
if (mag < other.InnerRadius() - this.radius ||
    mag > other.OuterRadius() + this.radius) {
    return false;
}

But then I need to check if the circle is inside or outside of the section defined by the two points. Closest I was able to get was to check if it isn't colliding with the start and end vectors, but this returns wrong results when the circle is fully inside the ring section.

auto dot1 = Vector::Dot(position, other.StartPoint());
auto projected1 = dot1 / Vector::Dot(other.StartPoint(), other.StartPoint()) * other.StartPoint();
auto distance1 = Vector::Distance(position, projected1);

auto dot2 = Vector::Dot(position, other.EndPoint());
auto projected2 = dot2 / Vector::Dot(other.EndPoint(), other.EndPoint()) * other.EndPoint();
auto distance2 = Vector::Distance(position, projected2);

return distance1 < radius || distance2 < radius;

What is the easiest way to check if a circle is colliding with a object defined by these two vectors?

enter image description here

Edit: all the point objects I'm using here are my custom Vector class that has implemented all the vector operations.

Edit2: just to clarify, the ring object has it's origin in [0, 0]

2
Could you be more specific about "all" the vector operations?Mad Physicist
dot and cross product, magnitude etc.Maroš Beťko
Sounds good. Could you draw a diagram? How are the segment's corners mapping to x, y? Why not use angles?Mad Physicist
I'm a bit confused too. If position is stored as a [x, y] vector, I don't understand how comparing its magnitude to the other object's radius tells you something meaningful: you'd get the same results for -this.position since its magnitude remains the same, but the position has moved somewhere else entirely. I may be misunderstanding this, but I hope that if I am, an explanation of how I'm reading your question will help you edit it into something more people will understand.user743382
@ma the start and end point are calculated on object construction and then recalculated when the object is rotated around the [0, 0]. I chose this approach because I think it's better to have this information saved rather than calculate it from angles every time I check collisions.Maroš Beťko

2 Answers

2
votes

Here is a simple algorithm.

First, let's agree on variable names:

meow

Here r1 ≤ r2, -π/2 ≤ a1 ≤ a2 ≤ π/2.

(As I was reminded in comments, you have start and end points rather than angles, but I'm going to use angles as they seem more convenient. You can easily obtain angles from points via atan2(y-ry, x-rx), just make sure that a1 ≤ a2. Or you can rewrite the algorithm to not use angles at all.)

We need to consider 3 different cases. The case depends on where the circle center is located relative to the ring segment:

meow

In the 1st case, as you already figured, collision occurs if length of vector (cx-rx, cy-ry) is greater than r1-rc and less than r2+rc.

In the 2nd case collision occurs if the distane between the circle center and the closest straight edge is less than rc.

In the 3rd case collision occurs if the distance between the circle center and the closest of 4 corners is less than rc.

Here's some pseudocode:

rpos = vec2(rx,ry); // Ring segment center coordinates
cpos = vec2(cx,cy); // Circle coordinates

a = atan2(cy-ry, cx-rx); // Relative angle
r = length(cpos - rpos); // Distance between centers

if (a > a1 && a < a2) // Case 1
{
    does_collide = (r+rc > a1 && r-rc < a2);
}
else
{
    // Ring segment corners:
    p11 = vec2(cos(a1), sin(a1)) * r1;
    p12 = vec2(cos(a1), sin(a1)) * r2;
    p21 = vec2(cos(a2), sin(a2)) * r1;
    p22 = vec2(cos(a2), sin(a2)) * r2;

    if (((cpos-p11) · (p12-p11) > 0 && (cpos-p12) · (p11-p12) > 0) ||
        ((cpos-p21) · (p22-p21) > 0 && (cpos-p22) · (p21-p22) > 0)) // Case 2
    {
        // Normals of straight edges:
        n1 = normalize(vec2(p12.y - p11.y, p11.x - p12.x));
        n2 = normalize(vec2(p21.y - p22.y, p22.x - p21.x));

        // Distances to edges:
        d1 = n1 · (cpos - p11);
        d2 = n2 · (cpos - p21);

        does_collide = (min(d1, d2) < rc);
    }
    else // Case 3
    {
        // Squared distances to corners
        c1 = length_sqr(cpos-p11);
        c2 = length_sqr(cpos-p12);
        c3 = length_sqr(cpos-p21);
        c4 = length_sqr(cpos-p22);

        does_collide = (sqrt(min(c1, c2, c3, c4)) < rc);
    }
}
0
votes

To compare the small circle to a ray:

First check to see whether the circle encloses the origin; if it does, then it intersects the ray. Otherwise, read on.

Consider the vector v from the origin to the center of the circle. Normalize that, normalize the ray R, and take the cross product Rxv. If it's positive, v is counterclockwise from R, otherwise it's clockwise from R. Either way, take acos to get the angle between them.

If the circle has radius r and its center is a distance d from the origin, then the angular half-width of the circle (as seen from the origin) is asin(r/d). If the angle between R and v is less than that, then the circle intersects the ray.

Assume that you know whether the object extends clockwise or counterclockwise from Start to End. (The numbers won't tell you that, you must know it already or the problem is unsolvable.) In your example, it's clockwise. Now you have to be careful; if the angular length of the arc is <= pi, then you can proceed, otherwise it is easier to determine whether the circle is in the smaller sector outside the sector of the object. But assuming the object spans less that pi, the circle is inside the sector of the object (i.e. between the rays) if and only if it is clockwise from the Start and counterclockwise from the End.