I want to make a collision detection between an ellipse and circle. The way I am doing this is:
However, I am having one small problem. When I calculate the angle, it seems like it is 90 degrees off. I did a dirty fix by simply adding 1.5 radians to account for the 90 degrees, and it kind of works but there are inconsistencies and doesn't work properly at certain angles, particularly at around 0.7 and -2.6 radians . Here is the code (all the collision stuff is in collision
method in Ellipse
class)
import pygame
from math import sin, cos, atan2, radians
pygame.init()
SW = 1200
SH = 600
WIN = pygame.display
D = WIN.set_mode((SW, SH))
class Circle:
def __init__(self, radius):
self.x = 0
self.y = 0
self.radius = radius
def update(self, pos):
self.x = pos[0]
self.y = pos[1]
def draw(self, display):
pygame.draw.circle(display, (255, 0, 0), (int(self.x), int(self.y)), self.radius, 2)
circle = Circle(30)
class Ellipse:
def __init__(self, centre, rx, ry):
self.centre = centre
self.collided = False
self.rx = rx
self.ry = ry
def draw(self, display):
angle = 0
while angle < 6.28:
angle += 0.001
x = self.centre[0] + sin(angle)* self.rx
y = self.centre[1] + cos(angle)* self.ry
if self.collided:
display.set_at((int(x), int(y)), (255, 0, 0))
else:
display.set_at((int(x), int(y)), (0, 0, 255))
pygame.draw.circle(D, (0, 255, 0), (int(self.centre[0]), int(self.centre[1])), 5)
def collision(self, circle):
#angle to the circle
dx = circle.x - self.centre[0]
dy = circle.y - self.centre[1]
angle = atan2(-dy, dx)
print(angle)
#where the point lies in the ellipse at that angle
x = sin(angle + 1.5)* self.rx + self.centre[0]
y = cos(angle + 1.5)* self.ry + self.centre[1]
#print(x, y)
#drawing the point just to make sure its working
# (debugging)
pygame.draw.circle(D, (0, 255, 0), (int(x), int(y)), 5)
# distance between the point we just
# calculated and the circle's centre
distance = ((x-circle.x)**2 + (y-circle.y)**2)**0.5
#print(distance)
#collision condition
if distance < circle.radius:
self.collided = True
else:
self.collided = False
ellipse = Ellipse([600, 300], 300, 200)
while True:
events = pygame.event.get()
mousePos = pygame.mouse.get_pos()
for event in events:
if event.type == pygame.QUIT:
pygame.quit()
D.fill((255, 255, 255))
circle.update(mousePos)
circle.draw(D)
ellipse.draw(D)
ellipse.collision(circle)
pygame.display.flip()
The fist mistake is, that 1.5 is not equal to pi/2:
from math import pi
x = sin(angle + 1.5)* self.rx + self.centre[0]
y = cos(angle + 1.5)* self.ry + self.centre[1]
x = sin(angle + pi/2)* self.rx + self.centre[0]
y = cos(angle + pi/2)* self.ry + self.centre[1]
Second, calculating the point on an ellipse by an angle is a little more complicated. See the answers to the questions How to get a point on an ellipse's outline given an angle? and Calculating a Point that lies on an Ellipse given an Angle:
from math import pi, sin, cos, atan2, radians, copysign, sqrt
class Ellipse:
# [...]
def pointFromAngle(self, a):
c = cos(a)
s = sin(a)
ta = s / c ## tan(a)
tt = ta * self.rx / self.ry ## tan(t)
d = 1. / sqrt(1. + tt * tt)
x = self.centre[0] + copysign(self.rx * d, c)
y = self.centre[1] - copysign(self.ry * tt * d, s)
return x, y
def collision(self, circle):
# [...]
#where the point lies in the ellipse at that angle
x, y = self.pointFromAngle(angle)
# [...]
Minimal example: repl.it/@Rabbid76/PyGame-IntersectCircleEllipse
import math
import pygame
class Circle:
def __init__(self, center_x, center_y, radius):
self.center = center_x, center_y
self.radius = radius
def update(self, center_x, center_y):
self.center = center_x, center_y
def draw(self, surface):
pygame.draw.circle(surface, (255, 0, 0), (round(self.center[0]), round(self.center[1])), self.radius, 3)
class Ellipse:
def __init__(self, center, vertex):
self.center = center
self.collided = False
self.vertex = vertex
def draw(self, surface):
bounding_rect = pygame.Rect(0, 0, self.vertex[0] * 2, self.vertex[1] * 2)
bounding_rect.center = round(self.center[0]), round(self.center[1])
pygame.draw.ellipse(surface, (0, 255, 0), bounding_rect, 3)
def pointFromAngle(self, a):
c = math.cos(a)
s = math.sin(a)
ta = s / c ## tan(a)
tt = ta * self.vertex[0] / self.vertex[1] ## tan(t)
d = 1. / math.sqrt(1. + tt * tt)
x = self.center[0] + math.copysign(self.vertex[0] * d, c)
y = self.center[1] - math.copysign(self.vertex[1] * tt * d, s)
return x, y
def intersect_circle_ellipse(circle, ellipse):
dx = circle.center[0] - ellipse.center[0]
dy = circle.center[1] - ellipse.center[1]
angle = math.atan2(-dy, dx)
x, y = ellipse.pointFromAngle(angle)
distance = math.hypot(x - circle.center[0], y-circle.center[1])
return distance <= circle.radius, (x, y)
pygame.init()
window = pygame.display.set_mode((500, 300))
circle = Circle(0, 0, 30)
ellipse = Ellipse(window.get_rect().center, (150, 100))
run = True
while run:
events = pygame.event.get()
mousePos = pygame.mouse.get_pos()
for event in events:
if event.type == pygame.QUIT:
run = False
circle.update(*mousePos)
isect = intersect_circle_ellipse(circle, ellipse)
window.fill((255, 255, 255))
circle.draw(window)
ellipse.draw(window)
color = (255, 0, 255) if isect[0] else (0, 0, 255)
pygame.draw.circle(window, color, (round(isect[1][0]), round(isect[1][1])), 5)
pygame.display.flip()
pygame.quit()
exit()