begin process at 2010 07 29 16:09:32
  Trouver un code source :
 
dans
 
Accueil > 

Code

 > 

Graphique

 > MANIPULATION À LA SOURIS D'UN GRAPHE SANS CIRCUIT (AVEC LA LIBRAIRIE PYGAME)

MANIPULATION À LA SOURIS D'UN GRAPHE SANS CIRCUIT (AVEC LA LIBRAIRIE PYGAME)


 Information sur la source

Note :
9,5 / 10 - par 2 personnes
9,50 / 10

  • 1

  • 2

  • 3

  • 4

  • 5

  • 6

  • 7

  • 8

  • 9

  • 10
Catégorie :Graphique Classé sous :graphe, pygame, pantin, articulation Niveau :Initié Date de création :10/07/2007 Date de mise à jour :12/07/2007 10:34:49 Vu :2 748

Auteur : TMONOD

Ecrire un message privé
Commentaire sur cette source (3)
Ajouter un commentaire et/ou une note

 Description

Cliquez pour voir la capture en taille normale
Dans l'optique de manipuler un assemblage d'images à la manière d'un pantin articulé, j'ai commencé par implémenter l'idée avec un graphe sans circuits, sous forme de segments articulés.

Ce module permet de déplacer à la souris chaque sommet du graphe, les distances entre les sommets sont respectées.

Le code est volontairement dépouillé afin de vous permettre de l'utiliser dans vos propres projets.

La partie la plus importante est l'algo qui calcule les nouvelles coordonnées des points.(methode move).
- La fonction s'appelle elle-même (récursivité). les nouvelles coordonnées des prédecesseurs et successeurs du point déplacé sont calculées récursivement avant la modification éffective du point. Cela évite d'implémenter une structure de données "pile" puisque les appels récursifs utilisent nativement une pile qui sauvegarde le contexte à la focntion lors de chaque appel.

- Le principe du calcul d'une nouvelle position d'un point A vers une position A' est le suivant :
pour chaque prédecesseur B du point A
    calculer les coordonnées du point A de manière que B A et A' soient alignés et l'ancienne distance BA soit                    respectée
    calculer en x et y les distances restant à parcourir au point A pour rejoindre la position A'.
    Apliquer ce déplacement au point B

Enfin :
Amusez-vous avec, ajoutez des points modifier le graphe, mes gosses ont "bloqué" dessus pendant une heure !!



Source

  • # -*- coding: cp1252 -*-
  • #Jean-Christophe Bouvard Juillet 2007
  • #nekochat@gmail.com
  • #déplacement graphique d'un graphe sans circuits
  • #a la manière d'un pantin articulé
  • #le graphe peut contenir des sous-graphes non connexes
  • import math
  • import os
  • import getopt
  • import pygame
  • from pygame.locals import *
  • class SimpleGraph:
  • def __init__(self,points ,arcs):
  • #liste de couples (x,y) convertis en float pour conserver la précision
  • self.points=points
  • for p in range(0,len(self.points)-1):
  • self.points[p] =intpoint2floatpoint(self.points[p])
  • #liste de tuples représentant l'ensemble des index de points adjacents
  • self.arcs=arcs
  • self.taglist=[]
  • #déplacer un point en déplacant l'ensemble des sommets de manière récursive
  • def move(self,pointindex, newpos):
  • mynewpos=intpoint2floatpoint(newpos)
  • self.taglist=[]
  • self.internalmove(pointindex, mynewpos)
  • self.taglist=[]
  • def internalmove(self,pointindex, mynewpos):
  • if not pointindex in self.taglist:
  • self.taglist.append(pointindex)
  • #parcours des successeurs.
  • for i in self.arcs[pointindex]:
  • #calcul des coordonnées du prédecesseur
  • #d'abord rotation du point autour du prédecesseur (en gardant la même distance) pour l'aligner
  • #avec la postion de destination
  • if i==pointindex:
  • print "redondance !" + str(i)
  • ivector=vectorfrom2points(self.points[i],mynewpos)
  • #rotatepoint=rotatesegment(self.points[i],mynewpos,ivector[0])
  • dist=distance(self.points[i],self.points[pointindex])
  • rotatepoint=(self.points[i][0]+ math.cos(ivector[0])*dist,self.points[i][1]+ math.sin(ivector[0])*dist)
  • #ensuite calcul des distances dx et dy à parcourir pour atteindre la position finale
  • deltapoint=(mynewpos[0]-rotatepoint[0],mynewpos[1]-rotatepoint[1])
  • #ajouter le mouvement de ce nouveau point dans la pile par un appel récursif à la fonction move
  • self.internalmove(i,(self.points[i][0]+deltapoint[0],self.points[i][1]+deltapoint[1]))
  • #parcour des predecesseurs
  • for i in range(0, len(self.points)):
  • if pointindex in self.arcs[i]:
  • #calcul des coordonnées du prédecesseur
  • #d'abord rotation du point autour du prédecesseur (en gardant la même distance) pour l'aligner
  • #avec la postion de destination
  • ivector=vectorfrom2points(self.points[i],mynewpos)
  • dist=distance(self.points[i],self.points[pointindex])
  • rotatepoint=(self.points[i][0]+ math.cos(ivector[0])*dist,self.points[i][1]+ math.sin(ivector[0])*dist)
  • #ensuite calcul des distances dx et dy à parcourir pour atteindre la position finale
  • deltapoint=(mynewpos[0]-rotatepoint[0],mynewpos[1]-rotatepoint[1])
  • #ajouter le mouvement de ce nouveau point dans la pile par un appel récursif à la fonction internalmove
  • self.internalmove(i,(self.points[i][0]+deltapoint[0],self.points[i][1]+deltapoint[1]))
  • #... et enfin deplacer le point
  • self.points[pointindex]=mynewpos
  • #recherche du sommet le plus proche d'une position donnée (surtout utilisée pour le pointage à la souris)
  • def nearestpoint(self,point):
  • best=0
  • for i in range(0, len(self.points)):
  • if distance(self.points[i],point)<distance(self.points[best],point):
  • best=i
  • return best
  • #Classe qui dessine une représentation d'une instance de notre SimpleGraph
  • class SimpleGraphDrawing:
  • def __init__(self,screen,simplegraph,arccolor=(0,0,0),pointcolor=(0,0,0)):
  • self.screen=screen
  • self.arccolor=arccolor
  • self.pointcolor=pointcolor
  • self.graph=simplegraph
  • #dessiner le graphe
  • def draw(self):
  • for point in self.graph.points:
  • indice=self.graph.points.index(point)
  • for arc in self.graph.arcs[indice]:
  • pygame.draw.line(self.screen, self.arccolor, floatpoint2intpoint(point),floatpoint2intpoint(self.graph.points[arc]), 1)
  • #un petit cercle autour de notre sommet pour faire joli
  • pygame.draw.circle(self.screen, self.pointcolor, floatpoint2intpoint(point), 5, 1)
  • #-----------------------------------------------------------------------------------------------
  • #Fonctions utiles dans la manipulation des sommets
  • #transformer un coupe d'entier en un couple de float
  • def intpoint2floatpoint(point):
  • return (float(point[0]),float(point[1]))
  • #...et un couple de float en entiers
  • def floatpoint2intpoint(point):
  • return (int(round(point[0])),int(round(point[1])))
  • #le point1 est le centre du cercle, le point2 est sur le cercle
  • #renvoie les coordonnées du point2 "déplacé" sur le cercle en accord avec l'angle
  • def rotatesegment(point1,point2,angle):
  • d=distance(point2,point1)
  • return (point1[0]+math.cos(angle)*d,point1[1]+math.sin(angle)*d)
  • #calculer la distance entre 2 points
  • def distance(floatpoint1,floatpoint2):
  • return abs(math.sqrt(math.pow(floatpoint2[0]-floatpoint1[0],2)+ math.pow(floatpoint2[1]-floatpoint1[1],2)))
  • #le point1 est le centre du cercle, le point2 est sur le cercle
  • #on renvoie les coordonnées polaires (angle, distance entre les 2 points)
  • def vectorfrom2points(floatpoint1,floatpoint2):
  • mydistance=distance(floatpoint1,floatpoint2)
  • point=(floatpoint2[0]-floatpoint1[0],floatpoint2[1]-floatpoint1[1])
  • if mydistance!=0.0:
  • if point[0]==0.0:
  • angle=(point[1]/abs(point[1]))*math.pi/2
  • else:
  • angle=math.atan(point[1]/point[0])
  • if point[0]<0.0:
  • angle=math.pi+angle
  • else:
  • print "distance=0 !"
  • angle=0
  • mydistance=0
  • return (angle,mydistance)
  • #-----------------------MAIN------------------------------------------------------------------------------------------------
  • def main():
  • pygame.init()
  • screen = pygame.display.set_mode((640, 480))
  • pygame.display.set_caption("manipuler un pantin")
  • #Création du fond
  • background = pygame.Surface(screen.get_size())
  • background = background.convert()
  • background.fill((250, 250, 250))
  • #données du graphe
  • points=[(75,125),(100,125),(150,175),(175,225),(200,175),(150,75),(200,100),(200,50), (320,250),(400,300),(425,450)]
  • arcs=[[1],[2,5],[3,4],[],[],[6,7],[],[],[9],[10],[]]
  • #création du graphe
  • mongraphe=SimpleGraph(points,arcs)
  • graphdraw=SimpleGraphDrawing(screen,mongraphe,(0,0,0),(250,45,45))
  • graphdraw.draw()
  • #Afficher le fond
  • screen.blit(background, (0, 0))
  • pygame.display.flip()
  • #Préparation...
  • clock = pygame.time.Clock()
  • #Main Loop--------------------------------------------------------------
  • #flag pour signifier que le déplacement d'un sommet est en cours
  • mousedown=False
  • while 1:
  • clock.tick(60)
  • #gestion des evennements
  • for event in pygame.event.get():
  • if event.type == QUIT:
  • return
  • #le bouton de la souris est enfoncée
  • #si il était levé on selectionne le sommet le plus proche
  • #qui suivra les mouvements de la souris jusqu'à qu'il soit relâché
  • elif event.type==MOUSEBUTTONDOWN:
  • if mousedown==False:
  • selectpoint=graphdraw.graph.nearestpoint(pygame.mouse.get_pos())
  • mousedown=True
  • elif event.type==MOUSEBUTTONUP:
  • mousedown=False
  • if mousedown==True:
  • graphdraw.graph.move(selectpoint,pygame.mouse.get_pos())
  • #Re-dessiner tout
  • screen.blit(background, (0, 0))
  • graphdraw.draw()
  • pygame.display.flip()
  • #------------------------------------------------------------------------------
  • #Appel de la fonction main lorsque le module est exécuté
  • if __name__ == '__main__': main()
# -*- coding: cp1252 -*-
#Jean-Christophe Bouvard Juillet 2007
#nekochat@gmail.com

#déplacement graphique d'un graphe sans circuits
#a la manière d'un pantin articulé
#le graphe peut contenir des sous-graphes non connexes

import math
import os
import getopt
import pygame
from pygame.locals import *


class SimpleGraph:
  def __init__(self,points ,arcs):
    #liste de couples (x,y) convertis en float pour conserver la précision
    self.points=points
    for p in range(0,len(self.points)-1):
      self.points[p] =intpoint2floatpoint(self.points[p])
    #liste de tuples représentant l'ensemble des index de points adjacents
    self.arcs=arcs
    self.taglist=[]
     
    
  #déplacer un point en déplacant l'ensemble des sommets de manière récursive
  def move(self,pointindex, newpos):
    mynewpos=intpoint2floatpoint(newpos)
    self.taglist=[]
    self.internalmove(pointindex, mynewpos)
    self.taglist=[]
    
  def internalmove(self,pointindex, mynewpos):
    if not pointindex in self.taglist:
      self.taglist.append(pointindex)
     
      #parcours des successeurs.
      for i in self.arcs[pointindex]:
        #calcul des coordonnées du prédecesseur
        #d'abord rotation du point autour du prédecesseur (en gardant la même distance) pour l'aligner 
        #avec la postion de destination
        if i==pointindex:
          print "redondance !" + str(i)
        ivector=vectorfrom2points(self.points[i],mynewpos)
        #rotatepoint=rotatesegment(self.points[i],mynewpos,ivector[0])
        dist=distance(self.points[i],self.points[pointindex])
        rotatepoint=(self.points[i][0]+ math.cos(ivector[0])*dist,self.points[i][1]+ math.sin(ivector[0])*dist)
        
        
        #ensuite calcul des distances dx et dy à parcourir pour atteindre la position finale
        deltapoint=(mynewpos[0]-rotatepoint[0],mynewpos[1]-rotatepoint[1])
        #ajouter le mouvement de ce nouveau point dans la pile par un appel récursif à la fonction move
        self.internalmove(i,(self.points[i][0]+deltapoint[0],self.points[i][1]+deltapoint[1]))
      #parcour des predecesseurs
      for i in range(0, len(self.points)):
        if pointindex in self.arcs[i]:
        #calcul des coordonnées du prédecesseur
        #d'abord rotation du point autour du prédecesseur (en gardant la même distance) pour l'aligner 
        #avec la postion de destination
          ivector=vectorfrom2points(self.points[i],mynewpos)
          dist=distance(self.points[i],self.points[pointindex])
          rotatepoint=(self.points[i][0]+ math.cos(ivector[0])*dist,self.points[i][1]+ math.sin(ivector[0])*dist)
          
        #ensuite calcul des distances dx et dy à parcourir pour atteindre la position finale
          deltapoint=(mynewpos[0]-rotatepoint[0],mynewpos[1]-rotatepoint[1])
          
        #ajouter le mouvement de ce nouveau point dans la pile par un appel récursif à la fonction internalmove
          self.internalmove(i,(self.points[i][0]+deltapoint[0],self.points[i][1]+deltapoint[1]))
      #... et enfin deplacer le point
      self.points[pointindex]=mynewpos
  
  #recherche du sommet le plus proche d'une position donnée (surtout utilisée pour le pointage à la souris)
  def nearestpoint(self,point):
    best=0
    for i in range(0, len(self.points)):
      if distance(self.points[i],point)<distance(self.points[best],point):
        best=i
    return best
      
#Classe qui dessine une représentation d'une instance de notre SimpleGraph
class SimpleGraphDrawing:
  def __init__(self,screen,simplegraph,arccolor=(0,0,0),pointcolor=(0,0,0)):
    self.screen=screen
    self.arccolor=arccolor
    self.pointcolor=pointcolor
    self.graph=simplegraph
  
  #dessiner le graphe
  def draw(self):
    for point in self.graph.points:
      indice=self.graph.points.index(point)
      
      for arc in self.graph.arcs[indice]:
        pygame.draw.line(self.screen, self.arccolor, floatpoint2intpoint(point),floatpoint2intpoint(self.graph.points[arc]), 1)
      #un petit cercle autour de notre sommet pour faire joli
      pygame.draw.circle(self.screen,  self.pointcolor, floatpoint2intpoint(point), 5, 1)

#-----------------------------------------------------------------------------------------------
#Fonctions utiles dans la manipulation des sommets

#transformer un coupe d'entier en un couple de float
def intpoint2floatpoint(point):
  return (float(point[0]),float(point[1]))


#...et un couple de float en entiers
def floatpoint2intpoint(point):
  return (int(round(point[0])),int(round(point[1])))


#le point1 est le centre du cercle, le point2 est sur le cercle
#renvoie les coordonnées du point2 "déplacé" sur le cercle en accord avec l'angle
def rotatesegment(point1,point2,angle):
  d=distance(point2,point1)
  return (point1[0]+math.cos(angle)*d,point1[1]+math.sin(angle)*d)

#calculer la distance entre 2 points
def distance(floatpoint1,floatpoint2):
  return abs(math.sqrt(math.pow(floatpoint2[0]-floatpoint1[0],2)+ math.pow(floatpoint2[1]-floatpoint1[1],2)))
  

#le point1 est le centre du cercle, le point2 est sur le cercle
#on renvoie les coordonnées polaires (angle, distance entre les 2 points)
def vectorfrom2points(floatpoint1,floatpoint2):
  mydistance=distance(floatpoint1,floatpoint2)
  point=(floatpoint2[0]-floatpoint1[0],floatpoint2[1]-floatpoint1[1])
  if mydistance!=0.0:
    if point[0]==0.0:
      angle=(point[1]/abs(point[1]))*math.pi/2
    else:
      angle=math.atan(point[1]/point[0])
      if point[0]<0.0:
        angle=math.pi+angle
  else:
    print "distance=0 !"
    angle=0
    mydistance=0
  return (angle,mydistance)

#-----------------------MAIN------------------------------------------------------------------------------------------------

def main():
 
  pygame.init()
  screen = pygame.display.set_mode((640, 480))
  pygame.display.set_caption("manipuler un pantin")
    

#Création du fond
  background = pygame.Surface(screen.get_size())
  background = background.convert()
  background.fill((250, 250, 250))

  #données du graphe
  points=[(75,125),(100,125),(150,175),(175,225),(200,175),(150,75),(200,100),(200,50), (320,250),(400,300),(425,450)]
  arcs=[[1],[2,5],[3,4],[],[],[6,7],[],[],[9],[10],[]]

  #création du graphe
  mongraphe=SimpleGraph(points,arcs)
  graphdraw=SimpleGraphDrawing(screen,mongraphe,(0,0,0),(250,45,45))
  graphdraw.draw()
#Afficher le fond
  screen.blit(background, (0, 0))
  pygame.display.flip()
#Préparation...
  clock = pygame.time.Clock()
   #Main Loop--------------------------------------------------------------
  
  #flag pour signifier que le déplacement d'un sommet est en cours
  mousedown=False
  while 1:
    clock.tick(60)

    #gestion des evennements
    for event in pygame.event.get():
      if event.type == QUIT:
        return
      #le bouton de la souris est enfoncée
      #si il était levé on selectionne le sommet le plus proche
      #qui suivra les mouvements de la souris jusqu'à qu'il soit relâché
      elif event.type==MOUSEBUTTONDOWN:
        if mousedown==False:
          selectpoint=graphdraw.graph.nearestpoint(pygame.mouse.get_pos())
          mousedown=True
      elif event.type==MOUSEBUTTONUP:
        mousedown=False
      if mousedown==True:
        graphdraw.graph.move(selectpoint,pygame.mouse.get_pos())
    #Re-dessiner tout
    screen.blit(background, (0, 0))
    graphdraw.draw()
    pygame.display.flip()
#------------------------------------------------------------------------------

#Appel de la fonction main lorsque le module est exécuté
if __name__ == '__main__': main()

 Conclusion

...A venir, une appli de création de séquences animées basées sur une succession de postures données à un pantin articulé dont les différentes parties sont des images.
Je planche également sur une solution pour appliquer l'algo avec un graphe dans lequel il y a des cycles.


 Historique

12 juillet 2007 10:34:49 :
Quelques fautes d'orthographe corrigées et une petite explication complémentaire de l'algo.

 Sources de la même categorie

Source avec Zip Source avec une capture PYGTK : CODES ET EXPLICATIONS POUR DÉBUTER par loloof64
TK_WATCH :HORLOGE GRAPHIQUE par afranck64
Source avec une capture DESSIN DE DÉS À ÉCHELLE VARIABLE SUR CANVAS par calogerogigante
BOITE DE CONNEXION USER/PASSWORD POUR PYTHON:TK_LOGIN par afranck64
Source avec Zip Source avec une capture CALCUL DE RÉSISTANCES par amaury74

 Sources en rapport avec celle ci

TROUVER TOUT LES QUADRILATÉRES POSSIBLES AVEC N POINTS ALEAT... par Buenol
Source avec Zip Source avec une capture EQUATION STANDARD DE LA DROITE par calogerogigante
Source avec Zip Source avec une capture PYSNAKE WITH PYGAME par beltegeuse
GRAPHES NON-ORIENTÉS par KimbleMandel
Source avec Zip Source avec une capture JEU DE TIR 360° AVEC PYGAME par 0KS0

Commentaires et avis

Commentaire de econs le 10/07/2007 18:01:51 administrateur CS

Originale cette petite source.
Pour la méthode nearestpoint, tu devrais mettre une limite de proximité, car tu pourrais cliquer très loin d'un point et quand même en sélectionner un.
L'usage voudrait qu'on ne vérifie cette proximité que dans un rayon restreint.

Commentaire de TMONOD le 13/07/2007 23:15:28

Merci, je penserais à modifier dans ce sens. Je planche sur le problème des cycles. La modif sera bientôt en ligne.

Commentaire de aera group le 30/08/2009 18:16:54 10/10

Je t'accorde un 10 mérité, bien que comme l'a dit Econs, il serait souhaitable de vérifié que le clic se fait à proximité d'un point ...
Il y a t il une avancée dans ce projet (jeu ou animation quelconque ???)

 Ajouter un commentaire


Discussions en rapport avec ce code source dans le forum

chemin le plus court dans un graphe [ par molly59 ] Bonjour, j'ai besoin de quelques conseils pour un travail en python sur un graphe. Nous devions faire une fonction qui donner tous les chemins qui


Nos sponsors


Sondage...

CalendriCode

Juillet 2010
LMMJVSD
   1234
567891011
12131415161718
19202122232425
262728293031 

Consulter la suite du CalendriCode

 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 7,410 sec (3)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales