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
Asked By: Moy

||

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()
Answered By: Rabbid76
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.