Moy
Moy

Reputation: 37

Trajectory plalnification

I have a program that generates circles and lines, where the circles can not collide with each other and the lines can not collide with the circles, the problem is that it only draws a line but not the others, it does not mark any error and as much as I think the reason I do not understand why, (I'm new to python, so excuse me if maybe the error is obvious)

I tried to remove the for from my CreaLin class and it does generate the lines but they all collide with the circles, I also thought that the collisionL function could be the problem since the method does not belong as such to the line class, but I need values from the circle class, so I don't know what would be another way to do it, I would like to know a method.

my code:

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, L2):
       l0 = [L2.x, L2.y]
       l1 = [L2.a, L2.b]
       cp = [self.cx, self.cy]

       x1 = l0[0] - cp[0]
       y1 = l0[1] - cp[1]
       x2 = l1[0] - cp[0]
       y2 = l1[1] - cp[1]
       dx = x2 - x1
       dy = y2 - y1
       dr = sqrt(dx*dx + dy*dy)
       D = x1 * y2 - x2 * y1
       discriminant = self.radio*self.radio*dr*dr - D*D
    
       if discriminant < 0:
          return False
       else:
          return True`

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

Result: enter image description here

Upvotes: 1

Views: 56

Answers (1)

Rabbid76
Rabbid76

Reputation: 210899

You need to implement the colisionC and colisionL methods in Linea and Circulo. See Problem with calculating line intersections for the line-line intersection algorithm. When checking for collisions between lines and circles, in addition to checking for collisions between circles and endless lines, you must also consider the beginning and end of the line segment:

class Linea:
    # [...]

    def colisionC(self, c2):
        return c2.colisionL(self)
    
    def colisionL(self, l1):
        return Linea.intersect_line_line((self.x, self.y), (self.a, self.b), (l1.x, l1.y), (l1.a, l1.b))
    
    def intersect_line_line(P0, P1, Q0, Q1):  
        d = (P1[0]-P0[0]) * (Q1[1]-Q0[1]) + (P1[1]-P0[1]) * (Q0[0]-Q1[0]) 
        if d == 0:
            return None
        t = ((Q0[0]-P0[0]) * (Q1[1]-Q0[1]) + (Q0[1]-P0[1]) * (Q0[0]-Q1[0])) / d
        u = ((Q0[0]-P0[0]) * (P1[1]-P0[1]) + (Q0[1]-P0[1]) * (P0[0]-P1[0])) / d
        if 0 <= t <= 1 and 0 <= u <= 1:
            return P1[0] * t + P0[0] * (1-t), P1[1] * t + P0[1] * (1-t)
        return None
class Circulo
    # [...]

    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, L2):
       l0 = [L2.x, L2.y]
       l1 = [L2.a, L2.b]
       cp = [self.cx, self.cy]
       x1 = l0[0] - cp[0]
       y1 = l0[1] - cp[1]
       x2 = l1[0] - cp[0]
       y2 = l1[1] - cp[1]
       if sqrt(x1*x1+y1*y1) < self.radio or sqrt(x2*x2+y2*y2) < self.radio:
           return True

       dx = x2 - x1
       dy = y2 - y1
       dr = sqrt(dx*dx + dy*dy)
       D = x1 * y2 - x2 * y1
       discriminant = self.radio*self.radio*dr*dr - D*D
       if discriminant < 0:
           return False
       
       sign = lambda x: -1 if x < 0 else 1
       xa = (D * dy + sign(dy) * dx * sqrt(discriminant)) / (dr * dr)
       xb = (D * dy - sign(dy) * dx * sqrt(discriminant)) / (dr * dr)
       ya = (-D * dx + abs(dy) * sqrt(discriminant)) / (dr * dr)
       yb = (-D * dx - abs(dy) * sqrt(discriminant)) / (dr * dr)
       ta = (xa-x1)*dx/dr + (ya-y1)*dy/dr
       tb = (xb-x1)*dx/dr + (yb-y1)*dy/dr
       return 0 < ta < dr or 0 < tb < dr

Put all objects into one container. Before you add a new Linea you have to check if the new "Line" intersects another object with "ColisionL". Before you add a new Circulo, you must check if the new "Line" intersects another object with CollisionC:

class CreaObjects:
    def __init__(self, figs):
        self.figs = figs
        self.no_lines = 0
        self.no_circles = 0
        
    def update(self):
        if self.no_circles <70:
            r = randint(5, 104)
            creOne = Circulo(randint(0, 600 - 2*r), randint(0, 400 - 2*r), r)
            if not any(fig.colisionC(creOne) for fig in self.figs):
                self.figs.append(creOne)
                self.no_circles += 1
    
        if self.no_lines <70:
            linOne = Linea(randint(0, 700), randint(0, 500), randint(0, 700), randint(0, 500))
            if not any(fig.colisionL(linOne) for fig in self.figs):
                self.figs.append(linOne)
                self.no_lines += 1 

Complete example:

import pygame as pg
from random import randint
from math import sqrt, hypot

class Linea:
    def __init__(self, x, y, a, b):
        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 colisionC(self, c2):
        return c2.colisionL(self)
    
    def colisionL(self, l1):
        return Linea.intersect_line_line((self.x, self.y), (self.a, self.b), (l1.x, l1.y), (l1.a, l1.b))
    
    def intersect_line_line(P0, P1, Q0, Q1):  
        d = (P1[0]-P0[0]) * (Q1[1]-Q0[1]) + (P1[1]-P0[1]) * (Q0[0]-Q1[0]) 
        if d == 0:
            return None
        t = ((Q0[0]-P0[0]) * (Q1[1]-Q0[1]) + (Q0[1]-P0[1]) * (Q0[0]-Q1[0])) / d
        u = ((Q0[0]-P0[0]) * (P1[1]-P0[1]) + (Q0[1]-P0[1]) * (P0[0]-P1[0])) / d
        if 0 <= t <= 1 and 0 <= u <= 1:
            return P1[0] * t + P0[0] * (1-t), P1[1] * t + P0[1] * (1-t)
        return None

class Circulo:
    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, L2):
       l0 = [L2.x, L2.y]
       l1 = [L2.a, L2.b]
       cp = [self.cx, self.cy]
       x1 = l0[0] - cp[0]
       y1 = l0[1] - cp[1]
       x2 = l1[0] - cp[0]
       y2 = l1[1] - cp[1]
       if sqrt(x1*x1+y1*y1) < self.radio or sqrt(x2*x2+y2*y2) < self.radio:
           return True

       dx = x2 - x1
       dy = y2 - y1
       dr = sqrt(dx*dx + dy*dy)
       D = x1 * y2 - x2 * y1
       discriminant = self.radio*self.radio*dr*dr - D*D
       if discriminant < 0:
           return False
       
       sign = lambda x: -1 if x < 0 else 1
       xa = (D * dy + sign(dy) * dx * sqrt(discriminant)) / (dr * dr)
       xb = (D * dy - sign(dy) * dx * sqrt(discriminant)) / (dr * dr)
       ya = (-D * dx + abs(dy) * sqrt(discriminant)) / (dr * dr)
       yb = (-D * dx - abs(dy) * sqrt(discriminant)) / (dr * dr)
       ta = (xa-x1)*dx/dr + (ya-y1)*dy/dr
       tb = (xb-x1)*dx/dr + (yb-y1)*dy/dr
       return 0 < ta < dr or 0 < tb < dr

class CreaObjects:
    def __init__(self, figs):
        self.figs = figs
        self.no_lines = 0
        self.no_circles = 0
        
    def update(self):
        if self.no_circles <70:
            r = randint(5, 104)
            creOne = Circulo(randint(0, 600 - 2*r), randint(0, 400 - 2*r), r)
            if not any(fig.colisionC(creOne) for fig in self.figs):
                self.figs.append(creOne)
                self.no_circles += 1
    
        if self.no_lines <70:
            linOne = Linea(randint(0, 700), randint(0, 500), randint(0, 700), randint(0, 500))
            if not any(fig.colisionL(linOne) for fig in self.figs):
                self.figs.append(linOne)
                self.no_lines += 1 

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.figs = []
        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()

    def run(self):
        cirCreater = CreaObjects(self.figs)
        while True:
            self.check_events()
            cirCreater.update()
            
            self.ven.fill(pg.Color('#404040'))
            for fig in self.figs:
                fig.dibujar(self.ven)
            pg.display.flip()
            self.reloj.tick(60)
                 
if __name__ == '__main__':
    ven = Ventana()
    ven.run()

Upvotes: 1

Related Questions