Archives par mot-clé : temps

Le processus génératif, retour sur les classes et le temps

Il est parfois utile de revoir ses bases et quoi de mieux qu’une fin d’année pour cela.
Durant cet exercice nous verrons comment réaliser un grand nombres de variations d’une même idée à l’aide du processus génératif.

Cet exercice nous permettra également de revoir les bases suivantes :

  • Création et utilisation des classes
  • Gestion du temps via un timer
  • Utilisation de coordonnées polaires

Définition du sketch 0

Notre skecth 0 sera la base de nos nombreuses variations. Il se traduit par un idée simple autour de laquelle nous pourrons facilement travailler.

Pour cela nous allons nous inspirer du travail de Joshua Nimoy et GMunk réalisé pour le film Tron Legacy et plus précisément l’arrière plan de la scène des « jeux ». Durant cette scène nous pouvons observer des feux d’artifices en arrière plan, ceux-ci présentent toutes les similitudes d’un feu d’artifice normal (étincelles, explosions, physique…) mais ont aussi la particularité d’être géométrique et ceux afin d’accentuer le fait que l’ensemble se passeau cœur d’un programme informatique.
Pour ce faire Joshua Nimoy a travaillé de nombreuses formes tel que les solides de planton comme l’icosaèdre ou le dodécaèdre.

Sur cette même idée nous allons réaliser un « feu d’artifice » géométrique. Notre skecth présentera un élément au comportement simple. Ce dernier effectuera en un temps déterminé un trajet. Lorsque son temps sera écoulé, ce dernier se stoppera et « explosera », laissant apparaître de nouveaux éléments ayant le même comportement et répartis sur un cercle.

expo

Pour réaliser cela nous aurons besoins d’un objet « Point » ayant un comportement défini et un certains nombre de variables.
Nous souhaitons que notre objet effectue un trajet vertical durant un temps donné avant que ce dernier ne s’arrête et « explose » en créant de nouveaux objets identiques à lui même. Enfin, afin que ces nouveaux points soit placés sur un cercle de centre « point explosé » nous aurons besoins de travailler en coordonnées polaires (cf cours trigonométrie).
Notre objet peut se décomposer de la manière suivante :

  • Variables
    • Positions x, y
    • Angle « theta »
    • Rayon « radius »
    • Vitesse du Rayon « vRadius » (cette variable nous permettra de déplacer notre Objet)
    • Valeur booléenne « vie » (Tant que le temps de vie de l’objet sera supérieur à 0 alors son état vie sera vrai)
    • Valeur booléenne « exploded » (Tant que notre objet n’aura pas explosé et engendré de nouveaux objets, alors son état exploded sera faux)
    • Taille « taille » de notre dessin
    • Valeur de margeX et margeY afin de positionner notre dessin sur la scène
    • Un objet Timer permettant de décompter la vie de l’objet
  • Méthodes
    • Methode « run » contrôlant l’ensemble des méthodes de l’objet
    • Methode « UpdateRadius » faisant parcourir ou non un chemin à l’objet en fonction de son état de vie
    • Méthode « displayPoint » affichant une ellipse à la position de l’objet
    • Méthode « displayLine » affichant une ligne depuis l’objet et son origine
  • Constructeur
    • Définitions du rayon, de l’angle et des marges
    • Initialisation des variables

Une fois l’ensemble de éléments composant notre objet défini, nous pouvons concevoir sa classe.
NB : Ici nous utilisons un timer afin de calculer le temps de vie de notre objet. Il ne faudra donc pas oublier d’inclure ce dernier dans notre sketch. Celui-ci est disponible ici

Nous allons désormais définir nos méthodes en commençant par la méthode permettant à notre objet de se mouvoir.
Notre objet possède un temps de vie durant lequel nous souhaitons qu’il se déplace. Lorsque ce temps de vie est épuisé notre objet devra s’arrêter. Pour cela nous allons créer une méthode « updateRadius » interrogeant le timer de notre objet. Il en résultera deux états :

  • Le timer est fini : Alors notre état de vie passe en faux
  • Le timer n’est pas fini : Alors nous incrémentons notre rayons de sa vitesse et mettons à jour les coordonnées de notre objet

Nous réaliserons ensuite deux méthodes permettant de dessiner notre objet ainsi que son parcours

Enfin nous assemblons l’ensemble des ces méthodes dans une méthode contrôleur

Nous venons alors de créer notre classe Point

Nous pouvons donc attaquer la seconde partie de notre sketch permettant d’appeler et afficher nos objets.
Nous savons que nous aurons besoins de dessiner un nombre encore non-défini d’objets Point.
Nous souhaitons aussi que nos objets explosent en créant un certains nombre de point. Nous définirons tout cela dans notre « setup ».

Afin de pouvoir recréer un nouveau « feu d’artifice » quand bon nous semble, nous allons créer un méthode « mousePressed » permettant de vider notre tableau et de re-initialiser nos variables.

Enfin dans notre méthode « draw » nous allons appeler, créer et afficher nos objets. Pour cela nous allons décomposer notre méthode en plusieurs parties

  • Une boucle for() nous permettant de parcourir notre tableau de points et à l’intérieur de laquelle nous effectuerons plusieurs taches
    • Appeler la méthode run() contrôlant nos objets
    • Interroger l’état de vie et d’explosion de notre objet
      • Si notre objet a fini son état de vie (vie == false) et n’a pas explosé (exploded == false) : alors nous créons de nouveaux objets dont l’origine sera la position de l’objet explosant. Enfin nous passerons l’état « exploded » de l’objet explosant en vrai

Si nous effectuons un « run » de notre skecth nous remarquons que nous obtenons l’effet souhaité, cependant notre sketch se met rapidement à ralentir puis se bloque. Cela s’explique par le fait que ce dernier effectue un calcul infini que la mémoire de l’ordinateur ne pourra supporter.

expo-bug

Si nous reprenons notre concept, nous remarquons que notre tableau d’objets augmente de manière exponentielle.
En effet chaque point, une fois sa vie terminée, explose en un nombre de points défini, par exemple 4.
Ainsi à chaque explosion (ou cycle) nous obtenons le calcul suivant : 1^4^4^4^4… soit : 4^nbDeCycle ou : mesPoints.size() = nbNewPts^nbDeCycle

Afin d’éviter à notre sketch de s’étendre à l’infini nous allons donc définir un nombre maximal de cycles auxquel notre tableau de points aura droit. Pour ce faire nous allons devoir compter les cycles de notre tableau, or nous disposons de 2 valeurs dans l’équation suivante : mesPoints.size() = nbNewPts^nbDeCycle soit mesPoints.size() et nbNewPts.

Pour calculer le nombre de cycles de notre tableau nous allons faire appel à une fonction bien pratique en Maths, la fonction logarithme népérien. En effet, en Maths, nous savons que log(a^x) = N ou a^N = x. Cela se note aussi de la manière suivante :
logNeperien

Nous pourrons alors traduire cela dans processing à l’aide des méthodes round() permettant de calculer un nombre arrondi et de la méthode log() permettant de calculer une valeur logarithmique. Ainsi nous aurons :

Il ne nous reste donc plus qu’à reporter cela dans notre sketch et de définir un nombre maximum de cycle pour notre tableau. Enfin dans notre méthode draw nous autorisons nos objets à se créer que si le nombre de cycle actuel n’est pas supérieur au nombre de cycle maximal. Nous rajoutons donc deux nouvelle variables :
cycle et limiteCycle. Notre « draw » se composera alors de la manière suivante :

Nous venons donc de terminer notre sketch 0, base de notre travail génératif.

ixd_processusgeneratif_00

Processus génératif

Nous venons de réaliser notre sketch 0, base de notre travail. Nous allons maintenant nous interroger sur comment réaliser des variations de cette idée. Pour cela rien de plus simple, ils nous suffit de nous interroger sur les variables, méthodes ou dessins que nous pouvons modifier afin de créer de nouveau sketch. C’est la base du processus génératif, prendre une idée et en réaliser de nombreuses variations en cherchant de nouvelles voies. Nous pouvons procéder de différentes manières :

  • En changeant des variables existantes
  • En changeant/rajoutant des comportements
  • en rajouter des objet dans la composition
  • En travaillant le design (composition, couleur, formes…)

Voici quelques pistes permettant des modifications possible afin de réaliser de nouveaux éléments

  • Afficher uniquement les objets points
  • Afficher uniquement les lignes des objets points
  • Dessiner les liaisons entre les objets point lorsque leur distance est inférieur à une valeur définie
  • Changer la forme des objets point
  • Définir une durée de vie aléatoire pour chaque objet
  • Définir une durée de vie aléatoire pour chaque cycle
  • Définir une durée de vie se décrémentant à chaque cycle
  • Définir une durée de vie se s’incrémentant à chaque cycle
  • Définir la vitesse des objet en aléatoire
  • Effectuer une rotation selon un angle défini ou en bruit perlin durant la course de l’objet
  • Positionner les nouveau objet sur un arc de cercle de 180°, 30°….
  • Dessiner en 3D
  • Ne dessiner les objet ou les ligne un cycle sur deux
  • Dessiner uniquement les objet pair et les ligne impaire
  • Incrémenter l’axe Z à chaque cycle
  • Si la durée de vie de l’objet est fini, il explose puis repart dans une autre direction aléatoire ou défini
  • Varier la taille des objets en fonction de leur distance les séparant du centre
  • … à vous de trouver la suite

Il est donc aisé, à partir d’une idée simple, de générer un grand nombre de variation de cette même idée.

L’ensemble des sources des visuels ci-dessus sont à retrouver dans le dossier DigitalLab – Revision sur gitHub

Le temps [Frames, horloges et décomptes]

Le temps est une notion essentielle dans un programme. Cette notion permet de connaitre le temps d’exécution d’un programme, à quelle vitesse ce dernier s’exécute, de découper des animations selon un certains timing mais aussi de prévoir des animations ou actions à des instants précis.

Nous verrons ici trois méthodes permettant d’appréhender le temps dans un programme : les frames, les horloges et le décompte.

Les frames

Les frames (ou images) sont directement inspiré du cinéma et de l’animation, ce sont les images affichées par secondes. En programmation une frame est une exécution complète de la boucle draw(), ainsi le nombre total de frames correspond au nombre total de fois que boucle draw() s’est effectuée. Les frames par seconde (ou FPS) permettent de connaitre la vitesse d’exécution du programme.

Processing permet d’obtenir rapidement le nombre total de frames effectuées ainsi que le nombre de frames par seconde à l’aide des méthodes suivantes :

Sachant que notre programme s’exécute à une vitesse moyenne de 60 frames par seconde nous pouvons donc obtenir le nombre de secondes durant lequel le programme s’est exécuté par un calcul simple :

Temps = frameCount/frameRate

Cependant cette méthode n’est pas conseillée compte tenu du fait qu’elle ne nous renverra jamais un temps réel.
En effet, au cours de son exécution le nombre de frames par seconde d’un programme peut grandement varier en fonction du nombre de calculs à effectuer. Nous pouvons d’ailleurs remarquer que ce dernier oscille entre des valeurs décimales comprises entre 58 et 60. Cette méthode n’est donc pas la plus juste lorsque nous souhaitons calculer un temps précis.

Dans l’exemple ci-dessous nous dessinons une ligne d’un bout à l’autre de la scène en 600 frames. Lorsque le programme atteint la frame 600, nous arrêtons la double draw() à l’aide de la méthode noLoop().

ixd_letemps_00

Les horloges

Une seconde méthode permettant de gérer du temps durant un programme consiste à se référer à l’horloge de la machine sur laquelle se dernier s’exécute. Cela permet, quelques soit la vitesse d’exécution du programme, de toujours obtenir une valeur de temps universelle et juste.

Processing nous permet de questionner l’horloge de manière très simple à l’aide des méthodes suivantes :

Nous noterons que les méthodes hour(), minute() et second() renvoi les valeur actuelles de l’horloge alors que la méthode millis() renvoi le temps d’exécution du programme en millisecondes.

Il est donc très facile, à partir de ces méthodes, de réaliser une horloge digitale.

ixd_letemps_01

Le décompte ou timer

Le décompte ou timer est une des méthodes les plus rependues permettant de gérer du temps lors de l’exécution d’un programme. Cette méthode permet de lancer un décompte en millisecondes. Pour cela nous utiliserons une class Timer. Cette classe permet de calculer une temps passé ainsi qu’un temps restant. Elle se construit de la manière suivante :

Les méthodes start(), stop() et reset() permettent respectivement de lancer arrêter ou réinitialiser le timer.
Les méthodes isFinished() et getRemainingTime() permettent quant à elles de savoir si le timer est fini et quelle est son temps passé en millisecondes.

Cette classe utilise un algorythme simpme permettant de décompter le temps.
Lorsque ce dernier est lancé, le timer sauvegarde le temps d’exécution actuel du programme à l’aide de la méthode millis() dans une variable correspondant au point de départ de notre Timer.
Enfin nous effectuons le calcul suivant :

TempsPassé = TempsactuelleEnMillisecondes – TempsDeDepart

Enfin si cette valeur est supérieur au temps total que nous souhaitons décompter alors notre timer est terminé.

Ainsi, pour créer un timer à l’aide de cet objet il nous suffira alors de créer un objet de type Timer, de le déclarer avec le temps que nous souhaitons décompter puis de le lancer à l’aide de la méthode start(). Nous pourrons ensuite, lors de l’exécution de la boucle draw() interroger l’état de notre timer (en cours ou fini) ainsi que son temps restant.

ixd_letemps_02

Un exemple d’horloge décomptant les secondes

Dans cet exemple nous allons réaliser une horloge décomptant le temps d’exécution du programme en minutes et secondes. Pour cela nous allons utiliser le concept simple du cadran de montre en secondes. À chaque minute passée nous dessinerons un nouveau cadran plus grand que le cadran précédent, chaque cadran se composant de cercles représentant les 60 secondes passées.

horloge

Pour cela nous aurons besoins de créer différentes classes : une classe cadran, que nous appellerons secondsDial, et une classe point qui permettra de dessiner un cercle par seconde sur notre cadran.

La classe point sera relativement simple et possédera peu de méthodes. Nous aurons besoins de connaitre une position x et y qui seront définies dans une méthode de la classe secondsDial. Nous définirons un taille ainsi qu’une couleur, enfin nous définirons une méthode display() qui dessinera nos points.

Notre classe cadran sera quant à elle un peu plus difficile à mettre en place. Nous aurons besoins des variables suivantes :

  • La position x,y du cadran
  • Le rayon du cadran
  • Un ArrayList d’objet Point (nos cercles)
  • Un compteur de cycle afin de savoir si notre cadran à atteint 60 secondes
  • Un décompte des secondes
  • Une limite de secondes

Afin de réaliser une horloge décomptant le temps d’exécution du programme nous utiliserons la méthode millis(). Celle-ci nous permettra de connaitre en millisecondes le temps d’exécution du programme alors que la méthode seconde() nous renvoi la valeur seconde de notre horloge machine (cf partie 03). Les milliscondes s’incrémentant nous aurons donc besoins de fixer une limite afin de savoir quand nous aurons atteint les 60 secondes pour chaque cadran. C’est la raison pour laquelle nous avons besoins ici d’une variable secondesLimites qui sera équale au temps actuel d’exécution du programme en millisecondes + 60000 millisecondes (soit 1 minute).

NB : Nous pourrions utiliser un timer afin de réaliser ces calculs mais nous verrons ici comment les réaliser « from scratch » afin de se familliariser avec la méthode millis() et le décompte d’un temps

Notre constructeur sera alors :

Afin de décompter les secondes de notre cadran nous réaliserons une méthodes updateSecondes nous permettant de mapper la valeur millis() en secondes pour notre cadran.

Nous réaliserons ensuite une méthodes booléenne nous permettant de savoir si notre cadran a atteint sa limite de décompte (60 secondes)

Une utiliserons ensuite une méthode update() afin d’ajouter un objet point à notre tableau dynamique à chaque seconde passée. Afin de définir la position de nos point sur notre cadran nous utiliserons les base de trigonométrie nous permettant de définir la position d’un point sur un cercle (cf cours trigonométrie)

Enfin nous réaliserons une méthode run regroupant nos mises à jour et notre dessin.

Une fois notre objet cadran réalisé il ne nous restera plus qu’à l’appeler dans notre boucle draw(). Sachant que nous souhaitons ajouter un cadran de plus en plus grand à chaque minutes nous aurons besoins de créer une tableau dynamique d’objet secondsDials. Enfin à l’aide d’une condition nous interrogerons le dernier cadran afin de savoir si son cycle est terminé, et ce à l’aide de sa méthode booléenne cycle(). Si ce dernier est fini nous ajouterons alors un nouveau cadran dont le rayon sera défini par le rayon du cadran inférieur + 10 pixels.
Enfin il ne faudra pas oublier d’afficher nos cadrans à l’aide de leur méthodes run()

ixd_letemps_03