TD 1 • windraw
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 le programme se ferme. Si vous passez sous les 50 pixels en , 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 ou .
É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 :
- algorithme naïf de tracé de segment ;
- algorithme de tracé de segment de Bresenham ;
- analyseur différentiel numérique.
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 qu’on appelle un cercle unitaire. cos d’un angle renvoie la composante du point sur le cercle unitaire. sin renvoie la composant 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 , 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
Sur ce cercle :
- soit un point valant
- soit un réel valant
- soit l’angle étant de radians
On peut calculer les composantes et de B en faisant :
Pour cet angle Il faudra donc placer un point en à 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.