line collision detector with circles
Question:
not long ago I am in python and I decided to make a path planning program, the truth cost me a lot since I am a beginner, but good to the point, is to generate circles of random size and position that do not collide with each other and then generate lines that do not collide with the circles, with much help and I managed to generate the circles without colliding but I can not generate the lines and much less that do not collide, I would greatly appreciate if you could help me and tell me what is wrong or missing
this is my code main:
import pygame as pg
from Circulo import Circulo
from Linea import Linea
from random import *
class CreaCir:
def __init__(self, figs):
self.figs = figs
def update(self):
if len(self.figs) <70:
choca = False
r = randint(5, 104)
x = randint(0, 600 + r)
y = randint(0, 400 + r)
creOne = Circulo(x, y, r)
for fig in (self.figs):
choca = creOne.colisionC(fig)
if choca == True:
break
if choca == False:
self.figs.append(creOne)
def dibujar(self, ventana):
pass
class CreaLin:
def __init__(self, lins):
self.lins = lins
def update(self):
if len(self.lins) <70:
choca = False
x = randint(0, 700)
y = randint(0, 500)
a = randint(0, 700)
b = randint(0, 500)
linOne = Linea(x, y, a, b)
for lin in (self.lins):
choca = linOne.colisionL(lin)
if choca == True:
break
if choca == False:
self.lins.append(linOne)
def dibujar(self, ventana):
pass
class Ventana:
def __init__(self, Ven_Tam= (700, 500)):
pg.init()
self.ven_tam = Ven_Tam
self.ven = pg.display.set_caption("Linea")
self.ven = pg.display.set_mode(self.ven_tam)
self.ven.fill(pg.Color('#404040'))
self.figs = []
self.lins = []
self.reloj = pg.time.Clock()
def check_events(self):
for event in pg.event.get():
if event.type == pg.QUIT or (event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE):
quit()
pg.display.flip()
def run(self):
cirCreater = CreaCir(self.figs)
linCreater = CreaLin(self.lins)
while True:
self.check_events()
cirCreater.update()
linCreater.update()
for fig in self.figs:
fig.dibujar(self.ven)
for lin in self.lins:
lin.dibujar(self.ven)
self.reloj.tick(60)
if __name__ == '__main__':
ven = Ventana()
ven.run()
class Circulo:
class Circulo(PosGeo):
def __init__(self, x, y, r):
self.x = x
self.y = y
self.radio = r
self.cx = x+r
self.cy = y+r
def __str__(self):
return f"Circulo, (X: {self.x}, Y: {self.y}), radio: {self.radio}"
def dibujar(self, ventana):
pg.draw.circle(ventana, "white", (self.cx, self.cy), self.radio, 1)
pg.draw.line(ventana, "white", (self.cx+2, self.cy+2),(self.cx-2, self.cy-2))
pg.draw.line(ventana, "white", (self.cx-2, self.cy+2),(self.cx+2, self.cy-2))
def update(self):
pass
def colisionC(self, c2):
return self.radio + c2.radio > sqrt(pow(self.cx - c2.cx, 2) + pow(self.cy - c2.cy, 2))
def colisionL(self, l1):
den = sqrt(l1.a*l1.a+l1.b*l1.b)
return abs(l1.a*self.cx+l1.b*self.cy+l1.c)/den < self.radio
and finally my class line:
class Linea(PosGeo):
def __init__(self, x, y, a, b):
super().__init__(x, y)
self.x = x
self.y = y
self.a = a
self.b = b
def dibujar(self, ventana):
pg.draw.line(ventana, "#7B0000", (self.x, self.y), (self.a, self.b))
def update(self):
pass
def colisionL(self, l1):
pass
Answers:
Your algorithm that detects the intersection of a line and a circle is wrong.A correct algorithm can be found all over the web. e.g. Circle-Line Intersection. A function that implements such an algorithm and finds the intersection points of a line and a circle could look like the following:
def sign(x):
return -1 if x < 0 else 1
def interectLineCircle(l1, l2, cpt, r):
x1 = l1[0] - cpt[0]
y1 = l1[1] - cpt[1]
x2 = l2[0] - cpt[0]
y2 = l2[1] - cpt[1]
dx = x2 - x1
dy = y2 - y1
dr = math.sqrt(dx*dx + dy*dy)
D = x1 * y2 - x2 * y1
discriminant = r*r*dr*dr - D*D
if discriminant < 0:
return []
if discriminant == 0:
return [((D * dy ) / (dr * dr) + cpt[0], (-D * dx ) / (dr * dr) + cpt[1])]
xa = (D * dy + sign(dy) * dx * math.sqrt(discriminant)) / (dr * dr)
xb = (D * dy - sign(dy) * dx * math.sqrt(discriminant)) / (dr * dr)
ya = (-D * dx + abs(dy) * math.sqrt(discriminant)) / (dr * dr)
yb = (-D * dx - abs(dy) * math.sqrt(discriminant)) / (dr * dr)
return [(xa + cpt[0], ya + cpt[1]), (xb + cpt[0], yb + cpt[1])]
If you just want to check if the line and the circle intersect, but you don’t need the intersection points, you can simply return discriminant > 0
:
def interectLineCircle(l1, l2, cpt, r):
x1 = l1[0] - cpt[0]
y1 = l1[1] - cpt[1]
x2 = l2[0] - cpt[0]
y2 = l2[1] - cpt[1]
dx = x2 - x1
dy = y2 - y1
dr = math.sqrt(dx*dx + dy*dy)
D = x1 * y2 - x2 * y1
discriminant = r*r*dr*dr - D*D
return discriminant > 0
Complete example:
import pygame, math
window = pygame.display.set_mode((500, 300))
l1 = [50, 0]
l2 = [450, 300]
r = 50
def sign(x):
return -1 if x < 0 else 1
def interectLineCircle(l1, l2, cpt, r):
x1 = l1[0] - cpt[0]
y1 = l1[1] - cpt[1]
x2 = l2[0] - cpt[0]
y2 = l2[1] - cpt[1]
dx = x2 - x1
dy = y2 - y1
dr = math.sqrt(dx*dx + dy*dy)
D = x1 * y2 - x2 * y1
discriminant = r*r*dr*dr - D*D
if discriminant < 0:
return []
if discriminant == 0:
return [((D * dy ) / (dr * dr) + cpt[0], (-D * dx ) / (dr * dr) + cpt[1])]
xa = (D * dy + sign(dy) * dx * math.sqrt(discriminant)) / (dr * dr)
xb = (D * dy - sign(dy) * dx * math.sqrt(discriminant)) / (dr * dr)
ya = (-D * dx + abs(dy) * math.sqrt(discriminant)) / (dr * dr)
yb = (-D * dx - abs(dy) * math.sqrt(discriminant)) / (dr * dr)
return [(xa + cpt[0], ya + cpt[1]), (xb + cpt[0], yb + cpt[1])]
clock = pygame.time.Clock()
run = True
while run:
clock.tick(250)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
cpt = pygame.mouse.get_pos()
isect = interectLineCircle(l1, l2, cpt, r)
window.fill("black")
pygame.draw.line(window, "white", l1, l2, 3)
pygame.draw.circle(window, "white", cpt, r, 3)
for p in isect:
pygame.draw.circle(window, "red", p, 5)
pygame.display.flip()
pygame.quit()
exit()
not long ago I am in python and I decided to make a path planning program, the truth cost me a lot since I am a beginner, but good to the point, is to generate circles of random size and position that do not collide with each other and then generate lines that do not collide with the circles, with much help and I managed to generate the circles without colliding but I can not generate the lines and much less that do not collide, I would greatly appreciate if you could help me and tell me what is wrong or missing
this is my code main:
import pygame as pg
from Circulo import Circulo
from Linea import Linea
from random import *
class CreaCir:
def __init__(self, figs):
self.figs = figs
def update(self):
if len(self.figs) <70:
choca = False
r = randint(5, 104)
x = randint(0, 600 + r)
y = randint(0, 400 + r)
creOne = Circulo(x, y, r)
for fig in (self.figs):
choca = creOne.colisionC(fig)
if choca == True:
break
if choca == False:
self.figs.append(creOne)
def dibujar(self, ventana):
pass
class CreaLin:
def __init__(self, lins):
self.lins = lins
def update(self):
if len(self.lins) <70:
choca = False
x = randint(0, 700)
y = randint(0, 500)
a = randint(0, 700)
b = randint(0, 500)
linOne = Linea(x, y, a, b)
for lin in (self.lins):
choca = linOne.colisionL(lin)
if choca == True:
break
if choca == False:
self.lins.append(linOne)
def dibujar(self, ventana):
pass
class Ventana:
def __init__(self, Ven_Tam= (700, 500)):
pg.init()
self.ven_tam = Ven_Tam
self.ven = pg.display.set_caption("Linea")
self.ven = pg.display.set_mode(self.ven_tam)
self.ven.fill(pg.Color('#404040'))
self.figs = []
self.lins = []
self.reloj = pg.time.Clock()
def check_events(self):
for event in pg.event.get():
if event.type == pg.QUIT or (event.type == pg.KEYDOWN and event.key == pg.K_ESCAPE):
quit()
pg.display.flip()
def run(self):
cirCreater = CreaCir(self.figs)
linCreater = CreaLin(self.lins)
while True:
self.check_events()
cirCreater.update()
linCreater.update()
for fig in self.figs:
fig.dibujar(self.ven)
for lin in self.lins:
lin.dibujar(self.ven)
self.reloj.tick(60)
if __name__ == '__main__':
ven = Ventana()
ven.run()
class Circulo:
class Circulo(PosGeo):
def __init__(self, x, y, r):
self.x = x
self.y = y
self.radio = r
self.cx = x+r
self.cy = y+r
def __str__(self):
return f"Circulo, (X: {self.x}, Y: {self.y}), radio: {self.radio}"
def dibujar(self, ventana):
pg.draw.circle(ventana, "white", (self.cx, self.cy), self.radio, 1)
pg.draw.line(ventana, "white", (self.cx+2, self.cy+2),(self.cx-2, self.cy-2))
pg.draw.line(ventana, "white", (self.cx-2, self.cy+2),(self.cx+2, self.cy-2))
def update(self):
pass
def colisionC(self, c2):
return self.radio + c2.radio > sqrt(pow(self.cx - c2.cx, 2) + pow(self.cy - c2.cy, 2))
def colisionL(self, l1):
den = sqrt(l1.a*l1.a+l1.b*l1.b)
return abs(l1.a*self.cx+l1.b*self.cy+l1.c)/den < self.radio
and finally my class line:
class Linea(PosGeo):
def __init__(self, x, y, a, b):
super().__init__(x, y)
self.x = x
self.y = y
self.a = a
self.b = b
def dibujar(self, ventana):
pg.draw.line(ventana, "#7B0000", (self.x, self.y), (self.a, self.b))
def update(self):
pass
def colisionL(self, l1):
pass
Your algorithm that detects the intersection of a line and a circle is wrong.A correct algorithm can be found all over the web. e.g. Circle-Line Intersection. A function that implements such an algorithm and finds the intersection points of a line and a circle could look like the following:
def sign(x):
return -1 if x < 0 else 1
def interectLineCircle(l1, l2, cpt, r):
x1 = l1[0] - cpt[0]
y1 = l1[1] - cpt[1]
x2 = l2[0] - cpt[0]
y2 = l2[1] - cpt[1]
dx = x2 - x1
dy = y2 - y1
dr = math.sqrt(dx*dx + dy*dy)
D = x1 * y2 - x2 * y1
discriminant = r*r*dr*dr - D*D
if discriminant < 0:
return []
if discriminant == 0:
return [((D * dy ) / (dr * dr) + cpt[0], (-D * dx ) / (dr * dr) + cpt[1])]
xa = (D * dy + sign(dy) * dx * math.sqrt(discriminant)) / (dr * dr)
xb = (D * dy - sign(dy) * dx * math.sqrt(discriminant)) / (dr * dr)
ya = (-D * dx + abs(dy) * math.sqrt(discriminant)) / (dr * dr)
yb = (-D * dx - abs(dy) * math.sqrt(discriminant)) / (dr * dr)
return [(xa + cpt[0], ya + cpt[1]), (xb + cpt[0], yb + cpt[1])]
If you just want to check if the line and the circle intersect, but you don’t need the intersection points, you can simply return discriminant > 0
:
def interectLineCircle(l1, l2, cpt, r):
x1 = l1[0] - cpt[0]
y1 = l1[1] - cpt[1]
x2 = l2[0] - cpt[0]
y2 = l2[1] - cpt[1]
dx = x2 - x1
dy = y2 - y1
dr = math.sqrt(dx*dx + dy*dy)
D = x1 * y2 - x2 * y1
discriminant = r*r*dr*dr - D*D
return discriminant > 0
Complete example:
import pygame, math
window = pygame.display.set_mode((500, 300))
l1 = [50, 0]
l2 = [450, 300]
r = 50
def sign(x):
return -1 if x < 0 else 1
def interectLineCircle(l1, l2, cpt, r):
x1 = l1[0] - cpt[0]
y1 = l1[1] - cpt[1]
x2 = l2[0] - cpt[0]
y2 = l2[1] - cpt[1]
dx = x2 - x1
dy = y2 - y1
dr = math.sqrt(dx*dx + dy*dy)
D = x1 * y2 - x2 * y1
discriminant = r*r*dr*dr - D*D
if discriminant < 0:
return []
if discriminant == 0:
return [((D * dy ) / (dr * dr) + cpt[0], (-D * dx ) / (dr * dr) + cpt[1])]
xa = (D * dy + sign(dy) * dx * math.sqrt(discriminant)) / (dr * dr)
xb = (D * dy - sign(dy) * dx * math.sqrt(discriminant)) / (dr * dr)
ya = (-D * dx + abs(dy) * math.sqrt(discriminant)) / (dr * dr)
yb = (-D * dx - abs(dy) * math.sqrt(discriminant)) / (dr * dr)
return [(xa + cpt[0], ya + cpt[1]), (xb + cpt[0], yb + cpt[1])]
clock = pygame.time.Clock()
run = True
while run:
clock.tick(250)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
cpt = pygame.mouse.get_pos()
isect = interectLineCircle(l1, l2, cpt, r)
window.fill("black")
pygame.draw.line(window, "white", l1, l2, 3)
pygame.draw.circle(window, "white", cpt, r, 3)
for p in isect:
pygame.draw.circle(window, "red", p, 5)
pygame.display.flip()
pygame.quit()
exit()