class Vecteur():

    def __init__(self):
        self.x=0
        self.y=0

    def normalize(self):
        d=self.x*self.x+self.y*self.y
        from math import sqrt
        d=sqrt(d)
        if (d!=0):
            self.x=self.x/d
            self.y=self.y/d

    def mult(self,coef):
        self.x=self.x*coef
        self.y=self.y*coef

    def add(self,v):
        res=Vecteur()
        res.x=self.x+v.x
        res.y=self.y+v.y
        return(res)

    def random(self):
        from random import uniform
        self.x=uniform(-1,1)
        self.y=uniform(-1,1)
        self.normalize()
        
        
        
        

#agent avec un objectif
class Agent():

    
    def __init__(self,dx,dy,coef):
        self.x=dx
        self.y=dy
        self.coeffs=coef

        self.vMax=20
        
        #par defaut pas de dynamique
        self.v=Vecteur()
        self.v.random()
        self.v.mult(self.vMax)
        
        self.ax=0
        self.ay=0
        self.dt=0.1
   
        
        #acceleration maximale
        self.aMax=20        

    def evoluer(self):
        self.v.x=self.v.x+self.ax*self.dt
        self.v.y=self.v.y+self.ay*self.dt
        self.x=self.x+self.v.x*self.dt
        self.y=self.y+self.v.y*self.dt

        if (self.x<0):
            self.x=0
            self.v.x=-self.v.x
        if (self.x>500):
            self.x=500
            self.v.x=-self.v.x


        if (self.y<0):
            self.y=0
            self.v.y=-self.v.y
        if (self.y>400):
            self.y=400
            self.v.y=-self.v.y
            


    def afficher(self):
        print("(x,y): "+str(self.x)+","+str(self.y)+"  vx,vy: "+str(self.vx)+","+str(self.vy))


   

    
    # BIZARRE : par inversement proportionnel à la distance
    # regle de separation
    def separation(self,voisins):
        v=Vecteur()
        for agent in voisins:
            v.x+=agent.x-self.x
            v.y+=agent.y-self.y
        v.x=-v.x
        v.y=-v.y
        v.normalize()
        return(v)


    # regle de cohesion
    def cohesion(self,voisins):
        v=Vecteur()
        for agent in voisins:
            v.x+=agent.x
            v.y+=agent.y
        v.x /= len(voisins)
        v.y /= len(voisins)
        res=Vecteur()
        res.x=v.x-self.x
        res.y=v.y-self.y
        res.normalize()
        return(res);
        
            

    # regle d'alignement
    def alignement(self,voisins):
        v=Vecteur()
        for agent in voisins:
            v.x+=agent.v.x
            v.y+=agent.v.y
        v.x /= len(voisins)
        v.y /= len(voisins)
        v.normalize()
        return(v);
        

   
    

    # fait du flocking
    def calculerObj(self,agents):
        
        #liste des voisins
        voisins=[]
        seuil=100
        
        #cherche les voisins
        for agent in agents:
            if (agent!=self) and (agent.distance(self)<seuil):
                voisins+=[agent]

        if (len(voisins)==0):
            return
        

        #applique les regles
        separation=self.separation(voisins)
        alignement=self.alignement(voisins)
        cohesion=self.cohesion(voisins)

        

        separation.mult(self.coeffs.coef_sep)
        alignement.mult(self.coeffs.coef_align)
        cohesion.mult(self.coeffs.coef_cohesion)
        
        res=separation.add(alignement).add(cohesion)
        self.v=self.v.add(res)


        #ajoute du bruit
        bruit=Vecteur()
        bruit.random()
        bruit.mult(self.coeffs.coef_bruit)
        self.v=self.v.add(bruit)
        
        
        self.v.normalize()
        self.v.mult(self.vMax)

        
        

        
            
    def distance(self,O):
        dx=(O.x-self.x)
        dy=(O.y-self.y)
        d2=dx*dx+dy*dy
        from math import sqrt
        return sqrt(d2)


class Coeff():
    def __init__(self):
        self.coef_sep=1
        self.coef_align=1
        self.coef_cohesion=1
        self.coef_bruit=0.5


# systeme c'est un agent et un objectif
class Systeme():

    #initialise le systeme avec les coordonnees de l'agent
    def __init__(self,num):
        from random import uniform
        self.num=num
        self.agents=[]
        self.coeffs=Coeff()

       
        for i in range(num):
            x=uniform(0,500)
            y=uniform(0,700)
            ag=Agent(x,y,self.coeffs)
            ag.groupe=1
            self.agents+=[ag]
      

    #evoluer le jeu
    def evoluer(self):
        for agent in self.agents:
            agent.calculerObj(self.agents)
            agent.evoluer()
        

    def dessiner(self):
        WHITE = (0xFF, 0xFF, 0xFF)
        RED = (0xFF, 0x00, 0x00)
        GREEN= (0x00, 0xFF, 0x00)
        PURPLE= (0xFF, 0x00, 0xFF)
        BLUE = (0x00, 0x00, 0xFF)
        BLACK = (0x00, 0x00, 0x00)
        screen.fill(WHITE)
        for agent in self.agents:
            pygame.draw.ellipse(screen,RED,(agent.x-5,agent.y-5,10,10))         
            pygame.draw.line(screen,BLACK,[agent.x,agent.y],[agent.x+agent.v.x,agent.y+agent.v.y])


print("Flocking simple")



           
import pygame
pygame.init()
size = (700, 500)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Moteur")


# boolean boucle
done = False
mouse=False

jeu=Systeme(50)
# horloge
clock = pygame.time.Clock()
# -------- boucle de jeu
while not done:
    # --- gestion evenements
    for event in pygame.event.get():
        
        # quitter
        if event.type == pygame.QUIT: 
            done = True

        if event.type == pygame.MOUSEBUTTONDOWN  :
            mouse = True
            ancienY=pygame.mouse.get_pos()[1]

        if event.type == pygame.MOUSEBUTTONUP  :
            mouse = False
    
    
        
        
            
       
            

            
    # --- evolution
    jeu.evoluer()
    
    # --- dessin
    jeu.dessiner()

    # on modifie coeff avec souris
    if (mouse):
        jeu.coeffs.coef_sep+=(pygame.mouse.get_pos()[1]-ancienY)/100
        ancienY=pygame.mouse.get_pos()[1]
        if (jeu.coeffs.coef_sep<0):
            jeu.coeffs.coef_sep=0
        font = pygame.font.Font(None, 36)
        texte="coeff_sep: "+str(jeu.coeffs.coef_sep)
        screen.blit(font.render(texte, True, (255,0,0)), (200, 100))
        
    # --- graphique
    pygame.display.flip()
    # --- attente
    clock.tick(60)

pygame.quit()
   

