Cartouche

Champ Valeur
Module Programmation Graphique en Rust
Auteur·e·s Élise Philippe (rédactrice) ; Eden Stocker (illustrateur)
Édition 2024-10-10
Durée 2~3 séances
Taille des équipes 2~3 personnes
Rendu via git, dépôt $YEAR_windraw, droits en lecture à delivery_collector
Nom du crate windraw

Règlement

Prenez connaissance du règlement ici

L’intégralité de la bibliothèque standard est autorisée. Vous êtes encouragés à vous servir des outils qu’elle vous propose s’ils résolvent un de vos problèmes. Votre objectif c’est d’éviter de ré-inventer la roue quand ça n’est pas nécessaire.

Certains crates sont proposés dans le sujet. Vous avez le droit de vous servir de toutes leurs fonctionnalités.

Vous pouvez rechercher des alternatives à ces crates, voir des crates qui vous proposent des fonctionnalités complètement différentes. Si ces crates affectent l’écriture de votre algorithme ou l’architecture de votre programme, vous devez les faire valider en amont.

Introduction

La programmation graphique !!! :D

Aujourd’hui vous allez ouvrir votre première fenêtre depuis un programme en rust, réagir à des événements claviers, tracer des formes. Que. Du. FUN !

En réalité une partie du travail a été faite pour vous. Vous allez utiliser un crate qui :

  • ouvre une fenêtre pour vous ;
  • gère le framerate/tickrate ;
  • gère les événements.

Ce sur quoi vous allez vous concentrer c’est :

  • de modifier le tableau de pixels pour qu’une image s’affiche ;
  • d’agir lorsqu’une touche est enfoncée.

Étape 0 : exemple

Commençons simple, vous n’allez pas avoir a écrire de code source. Créez un projet appelé window et ajoutez chinchilib ^0.2.1 en dépendance. Remplacez le main par défaut par celui dans la documentation du crate.

cargo add chinchilib@^0.2.1 env_logger log

Lancez votre programme et vous devriez pouvoir déplacer un petit pixel rouge avec les flèches de votre clavier. Si vous passez sous les 50 pixels en yy le programme se ferme. Si vous passez sous les 50 pixels en xx, le programme se halte. C’est du à la méthode done qui vous montre comment dire à chinchilib que votre application à terminé et veut que son résultat reste afficher (remain) ou qu’elle a terminé et veut que la fenêtre se ferme (exit).

Étape 1 : touches du clavier

Reconfigurer l’application MovingPixel de sorte qu’on puisse déplacer le pixel avec zqsd plutôt que les flèches directionnelles.

Étape 2 : condition de fin et sécurité

Modifiez l’application MovingPixel de sorte que le programme ne plante pas si le pixel est en dehors l’écran. Si le pixel essaie de dépasser une bordure (haut, bas, gauche, droite) il doit ré-apparaître à la bordure opposée.

Retirez aussi la condition qui fait s’arrêter ou se bloquer le programme lorsqu’on va sous les 50 xx ou yy.

Étape 3 : coordonnées

Écrivez une structure Coords2d et son implémentation. La structure doit servir à représenter des coordonnées en 2D en nombre entiers ou réels au moins. Elle doit donc être générique.

Implémentez les traits std::convert::Into et std::convert::From entre Coords<i32> et Coords<f32> en faisant une troncature lorsque c’est nécessaire. Le but est de pouvoir passer de coordonnées flottantes à non flottantes lorsque c’est nécessaire pour les calculs.

Implémentez sur Coords2d<f32> la méthode distance qui prend en paramètre une référence sur un second Coords2d<f32> et renvoie la distance entre les deux.

Étape 4 : tracé de ligne

Tracer des segments est une fonctionnalité essentielle à tout moteur de rendu graphique. Vous allez découvrir qu’il existe plusieurs types d’algorithmes pour le faire. Les plus simples ont tendance à ne pouvoir dessiner de jolies lignes que dans certaines conditions. Vous trouverez du pseudo code pour du tracé de ligne sur plusieurs pages Wikipédia comme :

Votre objectif est d’implémenter et de tester des algorithmes de tracé de segment. Vous pouvez rechercher des algorithmes où vous le souhaitez, ne vous limitez pas à Wiképédia ou à ces pages-ci.

Écrivez une fonction de tracé de ligne draw_line en public dans un module gfx qui prend en paramètre :

  • la framebuffer et ses dimensions ;
  • une position de départ ;
  • une position de fin ;
  • une couleur.

Modifiez l’application MovingPixel de sorte que :

  • lorsqu’on appuie sur E elle garde le point actuel en mémoire
  • lorsqu’on appuie sur A depuis un autre point, elle trace un segment entre celui-ci et le point gardé en mémoire.

Étape 5 : tracé de cercle

La même chose mais avec un cercle !

Énoncé

Dans le module gfx écrivez la fonction publique draw_circle qui prend en paramètre :

  • la framebuffer et ses dimensions ;
  • un point d’origine ;
  • un rayon ;
  • une couleur.

Cette fonction dessine un cercle avec un rayon et un point d’origine donné. Ce cercle doit avoir une épaisseur d’un seul pixel.

La méthode de tracé que je vous propose ici fait usage de f32::cos() et f32::sin(). Grâce à ces fonctions on peut obtenir une série de positions sur un cercle de rayon 1 et d’origine (0,0)(0, 0) qu’on appelle un cercle unitaire. cos d’un angle renvoie la composante xx du point sur le cercle unitaire. sin renvoie la composant yy du point. Si l’on souhaite un cercle d’un autre rayon que 1, il suffit de multiplier les deux composantes de chaque point trouvé par le rayon voulu.

Si on souhaite un cercle avec une origine différente de (0,0)(0, 0), il suffit d’ajouter aux composantes de chaque point les composantes de l’origine voulue.

Modifiez l’application MovingPixel de sorte que :

  • lorsqu’on appuie sur E elle garde le point actuel en mémoire
  • lorsqu’on appuie sur C depuis un autre point, elle trace le cercle avec la distance entre les deux points en rayon.

Exemple d’un point

Pasted image 20241118113121.png Sur ce cercle :

  • soit AA un point valant (4,5)(4,5)
  • soit rr un réel valant 6.86.8
  • soit l’angle OAB\sphericalangle{OAB} étant de 0.125pi0.125pi radians

On peut calculer les composantes xx et yy de B en faisant :

  • Bx=cos(OAB)r+AxBx= \cos(\sphericalangle{OAB}) * r + Ax
  • Bx=cos(0.125pi)6.8+4Bx=\cos(0.125pi) * 6.8 + 4
  • Bx0.99986.8+4Bx\approx 0.9998 * 6.8 + 4
  • Bx10.79984Bx\approx10.79984
  • By=sin(OAB)r+AyBy= \sin(\sphericalangle{OAB}) * r + Ay
  • By=sin(0.125pi)6.8+5By=\sin(0.125pi) * 6.8 + 5
  • By0.006856.8+5By\approx 0.00685 * 6.8 + 5
  • By5.04661By\approx5.04661

Pour cet angle Il faudra donc placer un point en (5,10)(5,10) à l’écran.

Pour tracer un cercle il faut calculer la même chose mais pour une ribambelle d’angles différents en utilisant une boucle.

Sur le cercle tracé vous allez vite remarquer que les points peuvent se retrouver espacés. Avec cet algorithme c’est normal. Une solution ça serait d’utiliser un incrément plus fin entre les points ce qui à pour effet de rendre votre algorithme plus lent.

Étape 6 : modulo

Lors du tracé du cercle, si vous dépassez la fenêtre en hauteur et en largeur, affichez les points qui dépassent de l’autre côté de la fenêtre en faisant un modulo des coordonnées.

Étape 7 : spirale

Dans le module gfx écrivez la fonction publique draw_spiral qui prend en paramètre :

  • la framebuffer et ses dimensions ;
  • un point d’origine ;
  • un rayon de départ ;
  • une couleur.

Cette fonction dessine une spirale avec un rayon et un point d’origine donné. La différence entre une spirale et un cercle, c’est que la spirale à un rayon variable et peut faire plusieurs tous complets.

Libre à vous décider des propriétés de la spirale ou de son nombre de tour. Vous pouvez même faire une fonction qui prend plus de paramètres que draw_spiral appelle avec des paramètres par défaut.