Accueil > > > 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
Description
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
Commentaires et avis
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
Pygame [ par Guillamue06 ]
Bonjour, j'ai un probleme avec pygame : import pygame, sys from pygame.locals import * J'ai une erreur au niveau de : from pygame.locals import *
graphe avec networkx [ par philam ]
Bonjour, je débute complétement en python et je voudrais faire un graphe de type réseau sémantique avec networkx (ou si quelqu'un a une librairie plu
Trace de trajectoire avec pygame [ par pg123 ]
Bonjour, J'utilise pyMunk et donc pygame pour faire des simulations physiques et j'aimerai pouvoir visualiser la trajectoire d'un corps. Par exemple,
|
Derniers Blogs
CSS CONTENT STATE SELECTORS (PERSONNAL DRAFT)CSS CONTENT STATE SELECTORS (PERSONNAL DRAFT) par FREMYCOMPANY
Bonjour à tous, Je viens de publier une proposition comprenant 5 pseudo-classes pour le CSS Working Group ayant trait à l'état de chargement d'un élément (ex: IMG,VIDEO,AUDIO,OBJECT pour l'HTML.). Si le c½ur vous en dit, vous pouvez retrouver cette p...
Cliquez pour lire la suite de l'article par FREMYCOMPANY MBA : POURQUOI FAIRE ET COMMENT LE CHOISIR ?MBA : POURQUOI FAIRE ET COMMENT LE CHOISIR ? par ROMELARD Fabrice
Formation initiale Durant la formation, le découpage classique est le suivant (je donnerai les équivalences Suisse lorsque je les connaîtrais) : Ecole primaire jusqu'au Collège : Formation générale permettant d'obtenir les méthodes...
Cliquez pour lire la suite de l'article par ROMELARD Fabrice Y'A DES ERREURS QUI PEUVENT RENDRE LE DéVELOPPEUR VIOLENTY'A DES ERREURS QUI PEUVENT RENDRE LE DéVELOPPEUR VIOLENT par Aleks
Quand on a ce genre d'erreur sans log :
Et bas on a juste envie de choper le gas de Microsoft qu'a développé ça et lui foutre des baffes de Coboye ! ...
Cliquez pour lire la suite de l'article par Aleks [HYPER-V 3] PRéSENTATION DES COMMANDLETS POWERSHELL[HYPER-V 3] PRéSENTATION DES COMMANDLETS POWERSHELL par Pierrick CATRO-BROUILLET
Avec la sortie prochaine de la Beta Consumer Preview de Windows 8, j'avais envie de revenir sur une des fonctionnalités que j'attends le plus et que, en bon geek que je suis, j'utilise déjà : Hyper-V 3 ainsi son module PowerShell.
Il y a déjà pléthor...
Cliquez pour lire la suite de l'article par Pierrick CATRO-BROUILLET IIS7 - COMPRESSION GZIPIIS7 - COMPRESSION GZIP par cyril
La compression GZIP permet d'améliorer les performances de navigation en compressant ce qu'envoie le serveur à un client. Pour comprendre comment cela fonctionne, regardons ce qu'il se passe au niveau HTTP lorsqu'un client tente d'accéder à une ress...
Cliquez pour lire la suite de l'article par cyril
Forum
PYVISA PROBLèMEPYVISA PROBLèME par sandrine44
Cliquez pour lire la suite par sandrine44
Logiciels
Easy-Planning (1.0.0.1)EASY-PLANNING (1.0.0.1)Basé sur les mêmes principes que MyPlanning, Easy-Planning permet de créer des plannings sous la ... Cliquez pour télécharger Easy-Planning Academy System (17.1.3.0)ACADEMY SYSTEM (17.1.3.0)Logiciel de gestion des établissements.
- élèves/étudiants (inscription, dossier, absence...)
-... Cliquez pour télécharger Academy System COLLECTOR PLUS (3.00B)COLLECTOR PLUS (3.00B)COLLECTOR PLUS version 3.00B est un logiciel utilisant une base de données alimentée par :
- L... Cliquez pour télécharger COLLECTOR PLUS PONAMEDIA PREMIUM - HELLLOOO FLASH DEMO (V7.4)PONAMEDIA PREMIUM - HELLLOOO FLASH DEMO (V7.4)PONAMEDIA TV DEVIENS HELLLOOO FLASH
LA TV SUR VOTRE ORDINATEUR.
Toute une plateforme Multi... Cliquez pour télécharger PONAMEDIA PREMIUM - HELLLOOO FLASH DEMO LettresFaciles 2011 (8.0.0.1)LETTRESFACILES 2011 (8.0.0.1)LettresFaciles est un logiciel facilitant la création et la rédaction de lettres types.
Son inte... Cliquez pour télécharger LettresFaciles 2011
|