Tochi Obudulu
Tochi Obudulu

Reputation: 54

How would I optimize this method for point of circle collision detection?

I am working on a python method with is supposed to return the coordinates of interception (the point of collision) for two touching circles. The method works as expected but it seems to be overtly expensive for its purpose.

There should only ever be one point of interception and the sizes of these circles can vary.

def getPointOfContact(self, body):
    a1 = min(self.pos.x, body.pos.x) # The x value of the centre of the left-most circle
    b1 = min(self.pos.y, body.pos.y) # The y value of the up-most circle
    a2 = max(self.pos.x, body.pos.x) # The x value of the right-most circle
    b2 = max(self.pos.y, body.pos.y) # The y value of the down-most circle
    dx = a2-a1 # (Delta x) The difference in x positions
    dy = b2-b1 # (Delta y) The difference in y positions
    if dy == 0: # In case of no difference in y,
        m = 0 # Gradient is "zero"
    elif dx == 0: # In case of no difference in x,
        m = float("inf") # Gradient is "infinity"
    else:
        m = dy/dx # Gradient
    if a1 == self.pos.x: # Such that left-most circle's radius is used
        r1 = (self.size.x)/2
    else:
        r1 = (body.size.x)/2
    # Calculates the x position of intersection using trig
    x = a1+(r1*math.cos(math.atan(m))) 
    if b1 == self.pos.y: # Such that up-most circle's radius is used
        r1 = (self.size.x)/2
    else:
        r1 = (body.size.x)/2
    # Calculates the y position of intersection using trig
    y = b1+(r1*math.sin(math.atan(m)))
    return (int(x), int(y)) # Returns the coordinates as a tuple of integers

The actual calculation is actually fairly straight forward. It simply adds the x and y components of the radii displacement to the coordinate of the centre of the circle.

The problem is that a lot has to be in place in order from the calculation to be valid, ie the circle used must have the smallest component and the radius (r1) much match to the circle.

Would there be a way of simplifying the method, or even less expensive technique altogether?

Thanks in advance.

Upvotes: 1

Views: 105

Answers (1)

Francis Colas
Francis Colas

Reputation: 3647

I'm not sure I get it. You have the radius of the first circle and you assume they are exactly tangent, right? Then just get rid of your cos, sin and atan:

d = sqrt(d1**2 + d2**2)
x = a1 + dx * r1 / d
y = b1 + dy * r1 / d

Look at this triangle where the + are the centers and * the intersection:

  0<-dx->|      
0 +
^  \ r1
|   \
|    *   d = r1+r2 = sqrt(dx**2 + dy**2)
dy    \ 
|      \ r2
v       \
-        +

Note 1: if m=dy/dx and you try to compute atan(m) then you'd better udirectly use atan2(dy, dx) which will take care of the annoying zeroes and infinity.

Note 2: cos(atan(dy/dx))==dx/R with R=sqrt(dx**2+dy**2) and same with sin and dy since atan(dy/dx) is the angle such that dx = R*cos(theta) and dy = R*sin(theta).

Note 3: you're casting things into int so your circles and intersections might be a bit off.

Upvotes: 1

Related Questions