4

I have a Circle with a center point (Center_X, Center_Y) and I am detecting if a rectangle falls into it's Radius (Radius). How would I be able to perform this task? I have tried using

if (X - Center_X)^2 + (Y - Center_Y)^2 < Radius^2:
        print(1)

Then I try to draw a circle to fit over this area:

Circle = pygame.draw.circle(Window, Blue, (Center_X, Center_Y), Radius, 0)

But it doesn't seem to line up. Is there something I am doing wrong?

Makoto
  • 104,088
  • 27
  • 192
  • 230
Gaspump1112
  • 103
  • 1
  • 2
  • 7
  • What are `X` and `Y`? – martineau Jul 13 '14 at 23:27
  • X and Y are the Center Points for the Enemy (EnemyCenter_X, EnemyCenter_Y) – Gaspump1112 Jul 13 '14 at 23:31
  • Rectangles define four corner points, so generally speaking, you would need to make sure all of them fall inside the circle. – martineau Jul 13 '14 at 23:52
  • I don't want it to test if the rectangle is entirely inside of the circle, just if they are colliding. – Gaspump1112 Jul 14 '14 at 00:03
  • In that case the title of you question should be changed. You still have to potentially check all four points, but you can quit testing as soon as one is found to be inside the circle. If any one is inside, it means they're colliding. – martineau Jul 14 '14 at 00:07
  • I still seem to be having the same problem. I have added in detection for the corners, but it still does nothing, should I add in more code so you can have a better look? – Gaspump1112 Jul 14 '14 at 00:20
  • 2
    @martineau The check that any of the 4 points is inside the circle is insufficient for a collision check though. – Michael Anderson Jul 14 '14 at 00:29
  • @Gaspump1112: I deleted my answer because it doesn't handle every possible case -- although it will work in many of them. I'll undelete it only if I can figure out how to make it deal with the other cases in an acceptable manner. – martineau Jul 14 '14 at 01:01
  • 2
    isn't this a duplicate of http://stackoverflow.com/questions/401847/circle-rectangle-collision-detection-intersection? – nonchip Jul 14 '14 at 01:02
  • @martineau In which other cases does it not work? Because I might be able to help. – Gaspump1112 Jul 14 '14 at 01:03
  • @Makoto I put [SOLVED] because the person who wrote the answer deleted it, but it had solved my question. So since I couldn't accept the answer, I just put [SOLVED] – Gaspump1112 Jul 14 '14 at 01:16
  • @nonchip It could be considered a duplicate but there are differences that could make it not a duplicate, such as the fact this question is specifically about Python, whereas the other question is about geometry. – Edward Dec 04 '15 at 22:10

3 Answers3

3

Here's what I was describing in my comments, plus changes to correct handling of the case of a circle inside a larger rectangle which Michael Anderson pointed out in a comment:

import math

def collision(rleft, rtop, width, height,   # rectangle definition
              center_x, center_y, radius):  # circle definition
    """ Detect collision between a rectangle and circle. """

    # complete boundbox of the rectangle
    rright, rbottom = rleft + width/2, rtop + height/2

    # bounding box of the circle
    cleft, ctop     = center_x-radius, center_y-radius
    cright, cbottom = center_x+radius, center_y+radius

    # trivial reject if bounding boxes do not intersect
    if rright < cleft or rleft > cright or rbottom < ctop or rtop > cbottom:
        return False  # no collision possible

    # check whether any point of rectangle is inside circle's radius
    for x in (rleft, rleft+width):
        for y in (rtop, rtop+height):
            # compare distance between circle's center point and each point of
            # the rectangle with the circle's radius
            if math.hypot(x-center_x, y-center_y) <= radius:
                return True  # collision detected

    # check if center of circle is inside rectangle
    if rleft <= center_x <= rright and rtop <= center_y <= rbottom:
        return True  # overlaid

    return False  # no collision detected
martineau
  • 119,623
  • 25
  • 170
  • 301
  • 2
    Consider a small circle inside a large box. This will return false. – Michael Anderson Jul 14 '14 at 00:41
  • Now you also miss the case where a small circle has its center just outside one of the edges of the box, (Thus having two crossing points on the one edge, but not including the vertices.) – Michael Anderson Jul 14 '14 at 03:11
  • @Michael: What do you mean by _also_ miss? – martineau Jul 14 '14 at 08:04
  • Maybe _also_ was wrong, "you still don't detected the case where..." would probably be more accurate – Michael Anderson Jul 14 '14 at 08:47
  • @Michael: Thanks for the clarification. As for the new failure case, while code probably could be added to handle it, at this juncture I think a better course would be to use one of the answers to the question [**Circle-Rectangle collision detection (intersection)**](http://stackoverflow.com/questions/401847/circle-rectangle-collision-detection-intersection). – martineau Jul 14 '14 at 14:23
  • Thanks for this really well thought out, working Python function which definitely works. It was just what I was looking for! – Edward Dec 04 '15 at 17:12
1

You have two common options for this kind of collision detection.

The first is to understand the ways two 2D objects can collide.

  1. A vertex of one can be inside the other
  2. Their sides can cross (even thought no verice is inside)
  3. One can be completely interior to the other.

Technically case 1. can only occur if case 2. also occurs, but it is often a cheaper check. Also case 3 is checked by case 1, in the case where both objects vertices are checked.

I would proceed like this. (as it is in order of cheapness)

  1. Check that their bounding boxes intersect.
  2. Check whether any vertex of the square is inside the
  3. Check if the center of the circle is inside the rectangle
  4. Check for circle - edge intersections.

The second and more general method is based on the notion of the product / expansion of shapes. This operation allows you to convert the intersection question into a point containment question.

In this case the circle / rectangle box intersection can be replaced with a check for a point in a rounded rectangle.

Michael Anderson
  • 70,661
  • 7
  • 134
  • 187
1

Use the dist function from Shortest distance between a point and a line segment

import math

def dist(p1, p2, c): 
    x1,y1 = p1
    x2,y2 = p2
    x3,y3 = c
    px = x2-x1
    py = y2-y1

    something = px*px + py*py

    u =  ((x3 - x1) * px + (y3 - y1) * py) / float(something)

    if u > 1:
        u = 1
    elif u < 0:
        u = 0

    x = x1 + u * px
    y = y1 + u * py

    dx = x - x3
    dy = y - y3

    dist = math.sqrt(dx*dx + dy*dy)

    return dist

Here is a test:

rect = [[0. ,  0. ],
       [ 0.2,  1. ],
       [ 2.2,  0.6],
       [ 2. , -0.4]]

c = 0.5, 2.0
r = 1.0

distances = [dist(rect[i], rect[j], c) for i, j in zip([0, 1, 2, 3], [1, 2, 3, 0])]
print distances
print any(d < r for d in distances)

output:

[1.044030650891055, 1.0394155162323753, 2.202271554554524, 2.0592194189509323]
False

Here is the plot:

enter image description here

Community
  • 1
  • 1
HYRY
  • 94,853
  • 25
  • 187
  • 187
  • As @Michael Anderson pointed out to me: Consider a small circle completely inside a large box. Not any distance calculated would be less than the radius, so your approach would return `False` for that case, too. In addition using `< r` means touching wouldn't be considered a collision. – martineau Jul 14 '14 at 02:03
  • I see, to detect the circle center is in the rectangle, you can use `point_in_poly()` from here: http://stackoverflow.com/questions/16625507/python-checking-if-point-is-inside-a-polygon – HYRY Jul 14 '14 at 02:56
  • Detecting a point in the rectangle doesn't require the generality of a `point_in_poly()` because in `pygame` they're all upright. A case where this even detecting that fails is where a small circle has its center just outside one of the edges of the box but still intersects it at (usually) two points. – martineau Jul 14 '14 at 13:01