un champs d'étoiles

 


Objectif : réaliser un champ d'étoiles par David Cobac


Premier pas

Le champ d'étoiles se caractérise par l'éloignement progressif des étoiles vers un point précis, le centre de l'écran par exemple. C'est-à-dire qu'on observe une diminution de la distance entre chaque étoile et ce centre.

Notre "espace" sera un simple canvas de la dimension de l'écran.

Les étoiles seront crées avec des ovales.

La diminution de l'éloignement sera réglée par un coefficient de réduction dans les deux directions x et y grâce à la commande scale.

Aspect global du code

Notre code aura à peu près cette allure là :

 proc main {l L nb epaisseur} {
         ...
         ...
         ...
 }
 wm resizable . false false
 wm geometry .  [winfo screenwidth .]x[winfo screenheight .]
 update
 set largeur [winfo width .]
 set hauteur [winfo height .]
 pack [canvas .c -width $largeur -height $hauteur -background black]
 main $largeur $hauteur 2 4

On reconnaît une procédure principale, la définition de notre fenêtre et de son contenu et l'appel pour finir de la procédure.

Notons que winfo screenwidth . permet de récupérer la largeur de l'écran et winfo screenheight . sa hauteur.

La procédure est appelée avec 4 arguments : les deux premiers sont associés à la taille de la fenêtre nb désignera le nombre d'étoiles à dessiner epaisseur désignera l'épaisseur (le rayon) de nos étoiles

Créons nos étoiles et éloignons les

Interressons nous à notre procédure main.

La création des étoiles se fait avec la commande create oval :

 for {set i 1} {$i<=$nb} {incr i} {
         set x [expr int(rand()*$l+1)]
         set y [expr int(rand()*$L+1)]
         .c create oval [expr $x-$epaisseur] [expr $y-$epaisseur] [expr $x+$epaisseur]\
          [expr $y+$epaisseur] -fill white -outline white -tags etoile
 }

Nos étoiles sont tout simplement prises au hasard sur notre canevas. L'étoile elle même est un cercle.

Voilà vos étoiles tracées mais hélas bien statiques sur votre écran... Pour les éloigner, nous allons réduire le contenu de notre canevas en le rapprochant du point milieu : '''

 .c scale etoile [expr $l/2] [expr $L/2] .95 .95

Le coefficient de réduction est ici de 0.95(=95%) dans les deux directions (on peut d'ailleurs différencier les coefficients sur les deux axes pour avoir différents effets).

Le taggage par le mot etoile permet de n'agir que sur tous les objets etoile si jamais vous décidez de mettre autre chose sur votre canevas.

Le code est devenu alors celui-là :

 proc main {l L nb epaisseur} {
         for {set i 1} {$i<=$nb} {incr i} {
                 set x [expr int(rand()*$l+1)]
                 set y [expr int(rand()*$L+1)]
                 .c create oval [expr $x-$epaisseur] [expr $y-$epaisseur] [expr $x+$epaisseur]\
                  [expr $y+$epaisseur] -fill white -outline white -tags etoile
         }
         .c scale etoile [expr $l/2] [expr $L/2] .95 .95
 }
 wm resizable . false false
 wm geometry .  [winfo screenwidth .]x[winfo screenheight .]
 update
 set largeur [winfo width .]
 set hauteur [winfo height .]
 pack [canvas .c -width $largeur -height $hauteur -background black]
 main $largeur $hauteur 2 4

Le mouvement

Nous venons donc de réaliser des étoiles qui se voient immédiatement attirer par le centre de l'écran...une fois !

Nous allons alors répéter ce processus : création + réduction.

Le détail important est que la réduction s'appliquant à tous les objets taggés par le mot etoile, elle va aussi s'appliquer aux objets etoile qui ont déjà été réduits, créant ainsi une impression de mouvement.

Pour réaliser plusieurs fois de suite la procédure main nous allons utiliser la commande after qui permet de régler le temps en ms pour appeler la procédure. Nous allons mettre cette commande dans notre procédure : une boucle sans fin. Voilà donc notre premier champ d'étoiles :

 proc main {l L nb epaisseur} {
         for {set i 1} {$i<=$nb} {incr i} {
                 set x [expr int(rand()*$l+1)]
                 set y [expr int(rand()*$L+1)]
                 .c create oval [expr $x-$epaisseur] [expr $y-$epaisseur] [expr $x+$epaisseur]\
                  [expr $y+$epaisseur] -fill white -outline white -tags etoile
         }
         .c scale etoile [expr $l/2] [expr $L/2] .95 .95
         after 50 "main $l $L $nb $epaisseur"
 }
 wm resizable . false false
 wm geometry .  [winfo screenwidth .]x[winfo screenheight .]
 update
 set largeur [winfo width .]
 set hauteur [winfo height .]
 pack [canvas .c -width $largeur -height $hauteur -background black]
 main $largeur $hauteur 2 4

Le résultat est spectaculaire : en 19 lignes nous avons notre champ d'étoiles !

Le détail qui tue

Activons notre champ d'étoiles, et intéressons nous à la place occupée par ce processus : on s'aperçoit bien vite que la place occuppée grandit au fur et à mesure, 2.5Mo, puis rapidement 3Mo et ça continue et en même temps votre champ devient incroyablement lent ! Il serait facile d'incriminer Tcl/Tk mais voyons un peu le code : le nombre d'étoiles augmente tous les 50ms et toutes ces étoiles sont déplacées, au début l'ordinateur n'a pas beaucoup de problème car très peu d'étoiles existent mais au bout de quelques secondes... leur nombre est effrayant !!

La remédiation est fort simple, il suffit d'éliminer les étoiles qui ne servent à plus rien : les étoiles qui sont tellement éloignés qu'elles se confondent avec le centre de votre écran.

Pour cela nous allons dans un premier temps les identifier puis les éliminer avant de faire notre after. TcL/Tk dispose de la commande addtag qui permet de rajouter un tag à un objet existant dans le canevas, les objets que nous choisirons sont ceux compris dans un carré de côté 3 pixels dont le centre est le centre de l'écran.

La manip sera invisible à l'écran tellement les choses vont vite, bien entendu, choisir un carré plus grand vous le fera apparaître !

 .c addtag adetruire enclosed [expr $l/2-1] [expr $L/2-1]\
  [expr $l/2+1] [expr $L/2+1]
 .c delete adetruire

Bonne surprise, le code ci-dessus ajouté juste avant le after ne ralentit plus le champ d'étoiles, le processus n'augmente plus de taille, aux environs de 2.5Mo.

Modifications

En jouant sur le temps du after, sur le nombre de points, sur l'épaisseur, sur la réduction (les réductions devrais-je dire), sur la taille de la fenêtre initiale (ici on a pris tout l'écran) on peut influencer les performances et le comportement du champ. Le code à télécharger contient des accolades dans tous les calculs ce qui améliorent encore plus les performances.