deutsch     english    français     Imprimer

 

3.5 VARIABLES GLOBALES ET ANIMATIONS

 

 

INTRODUCTION

 

Les graphiques par ordinateur sont souvent utilisés pour représenter des données variant avec le temps. On s’en sert par exemple pour simuler un processus physique ou biologique ou encore pour créer des jeux. On parle alors généralement d’animation. Pour montrer une séquence temporelle, de nouvelles images sont dessinées les unes après les autres, espacées de manière régulière, ce que l’on nomme pas d’animation.

CONCEPTS DE PROGRAMMATION: Variables globales, effets de bord, double buffering

 

 

MODIFIER LES VARIABLES GLOBALES

 

On aimerait illustrer une balle qui se déplace sur un cercle. On obtient un mouvement circulaire de rayon 1 en calculant la coordonnée x = cos(t) et y = sin(t)t est un paramètre croissant correspond au temps qui s’écoule. Si l’on désire un autre rayon que 1, il faut multiplier chacune des coordonnées par le rayon désiré.

La fonction step() dessine la situation pour chaque pas de l’animation. Une fois que la balle a parcouru le cercle entièrement, on voudrait changer sa couleur.
 

Il est très courant d’introduire une boucle d'animation infinie dans le programme principal dont le rôle est d’appeler la fonction step() de manière répétitive. En terminant cette boucle par un  délai, on peut définir les pauses à marquer entre chaque étape de l’animation et de ce fait régler la vitesse d’animation. La fonction step() incrémente la variable globale tà chaque pas de la simulation et la réinitialise à 0 une fois le tour complet effectué. On sait que le tour est terminé lorsque t >= 2*pi = 2 * 3.14 = 6.28. Une fois le tour terminé, on change de couleur.

import math
from gpanel import *

def step():
    global t
    x = r * math.cos(t)
    y = r * math.sin(t)
    t = t + 0.1
    if t > 6.28:
        t = 0
        setColor(getRandomX11Color())
    move(x, y)
    fillCircle(10)

makeGPanel(-500, 500, -500, 500)
bgColor("darkgreen")

t = 0
r = 200 

while True:
    step()
    delay(10)
Sélectionner le code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

MEMENTO

 

Python ne permet pas de changer la valeur des variables globales de l’intérieur d’une fonction, car cela pourrait poser de nombreux problèmes qu’il faut éviter à tout prix. On peut cependant faire sauter cette sécurité en déclarant la variable comme globale au sein de la fonction à l’aide du mot-clé global.

Ce mot-clé global doit être utilisé avec la plus grande prudence car il implique un risque dont il faut être conscient : toute fonction peut non seulement modifier une variable déclarée comme globale, mais il peut également la créer comme le montre l’exemple suivant ::

def set():
    global a
    a = 2

def get():
    print "a =", a
set()
get()

Du fait que set() engendre une variable a qui est visible dans tout le programme, on dit que la fonction set() engendre des effets de bord.

Remarquez en passant la manière dont la commande print permet d’écrire plusieurs choses différentes dans la fenêtre de sortie en les séparant par des virgules. Dans la sortie, les virgules sont remplacées par des espaces.

 

 

ANIMATIONS FLUIDES

 

Pour que l’animation soit fluide et régulière, il faut s’arranger pour que les pauses entre chaque étape de l’animation soient de même durée, sans quoi le mouvement risque d’apparaître saccadé. La fonction step() permet de préparer la prochaine étape de l’animation. Le temps d’exécution de cette opération peut varier selon la situation, du fait que le programme n’exécute pas toujours les mêmes parties du code d’une part et surtout parce que le processeur peut être plus ou moins occupé à d’autres tâches de fond qui pourraient retarder l’exécution du code Python à certains moments. Pour compenser cette variation dans la durée d’exécution de step(), on peut utiliser l’astuce suivante à laquelle vous avez peut-être déjà pensé : avant d'appeler step(), on mémorise le temps actuel dans la variable startTime. Après avoir terminé l’exécution de step(), on attend dans une boucle d’attente jusqu’à ce que la difference entre l’heure actuelle et l’heure précédemment mémorisée corresponde à la période d’attente désirée entre chaque étape de l’animation.

Le programme suivant anime un ballon de football se déplaçant d’un but à l’autre. Pour ce faire, il utilise l’image football.gif situé dans le dossier sprites de l’archive TigerJython. Vous pouvez naturellement utiliser une image personnalisée en la sauvant dans le dossier approprié de votre ordinateur et en passant en tant que paramètre de la fonction image() son chemin absolu ou relatif par rapport à l’emplacement de votre programme Python.

 
from gpanel import *
import time

def step():
    global x
    global v
    clear()
    lineWidth(5)
    move(25, 300)
    rectangle(50, 100)
    move(575, 300)
    rectangle(50, 100)
    x = x + v
    image("_sprites/football.gif", x, 275)
    if x > 500 or x < 50:
        v = -v

makeGPanel(0, 600, 0, 600)
bgColor("forestgreen")
enableRepaint(False)

x = 300
v = 10

while True:
    startTime = time.clock()
    step()
    repaint()
    while (time.clock() - startTime)  < 0.020:
        delay(1)
Sélectionner le code (Ctrl+C pour copier, Ctrl+V pour coller)

 

 

MEMENTO

 

Avec time.clock(), on peut récupérer l’heure actuelle sous forme d’un nombre décimal. La valeur retournée est dépendante de l’ordinateur et du système d’exploitation. Dans certains environnements, time.clock() retourne le temps processeur et dans d’autres le temps écoulé depuis le premier appel à clock(). Mais comme on ne s’intéresse qu’à la différence entre les deux mesures du temps, cela n’a pas d’importance. Il suffit de mémoriser l’heure avant l’appel de step() et d’attendre à la fin de boucle d’animation, avec delay(1), que la différence entre l’heure actuelle et celle mémorisée dans startTime corresponde environ à la période d’animation désirée. Notez cette astuce car vous allez devoir la réutiliser à chaque fois qu’il faudra faire quelque chose à intervalles réguliers.

Chaque commande graphique est immédiatement visible dans la fenêtre GPanel. De ce fait, effacer avec clear() pendant une animation va brièvement faire apparaître une fenêtre vide, ce qui cause un clignotement de l’animation désagréable. Pour éviter ce phénomène, il faut utiliser la technique du double buffering lors du rendu de nos animations.

Cette technique consiste à utiliser l’instruction enableRepaint(False), qui permet d’effectuer le rendu graphique dans une mémoire tampon (buffer en anglais) hors écran et de n’afficher l’image que lorsque le rendu est terminé, ce qui réduit considérablement les effets de clignotement indésirables. Ainsi, la fonction clear() n’affecte que le tampon du rendu graphique et n’efface plus le contenu de la fenêtre graphique. Il faut par contre prendre soin d’afficher manuellement le contenu du tampon de rendu sur l’écran au moment opportun en appelant repaint().

Il faut veiller également à déclarer les variables x et v  comme globales au sein de la fonction step() puisqu’il s’agit de variables globales modifiées depuis l’intérieur de la fonction.

 

 

EXERCICES

 
1.

Si l’on ne déplace pas les coordonnées x et y en utilisant les fonctions sinus et cosinus ordinaires, comme dans le premier programme, mais avec des variations de t différentes, cela va engendrer des courbes intéressantes appelées courbes de Lissajoux. Dessiner de telles courbes avec une résolution de 1/1000 pour t dans l’intervalle [0, 2π]  et avec les coordonnées

x = cos(4.5 * t) und y = sin(6.3 * t)


2.


Au lieu d’utiliser des valeurs fixes pour le coefficient de t, utiliser des variables omega_x et omega_y prenant les valeurs suivantes :

omega_x
omega_y
3
5
3
7
5
7

Y a-t-il un lien entre les figures dessinées et les valeurs prises par omega_x et omega_y ?


3.


Dessiner, dans un GPanel dont les deux axes s’étendent entre -2 et 2, les courbes de Lissajoux suivantes : prendre omega_x = 2 et omega_y = 7, pour t dans l’intervalle [0, 2π], avec une résolution de 1/100. Au lieu de relier les points de la courbe avec des segments droits, dessiner en chaque point de l’animation un disque de rayon 0.2. On obtient alors une image qui fait penser à un slinky. Comme le montre l’illustration ci-dessous, on peut utiliser des couleurs aléatoires pour chaque disque avec getRandomX11Color(). N’hésitez pas à vous amuser avec cet exercice !