From d7cdf8cec9966cb92540098497c90ec963bd26b5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nicolas=20M=2E=20Thi=C3=A9ry?= <nthiery@users.sf.net>
Date: Sun, 26 Nov 2023 16:55:19 +0100
Subject: [PATCH] =?UTF-8?q?Pr=C3=A9paration?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 .gradebook.db           | Bin 192512 -> 192512 bytes
 exemple-graphisme1.cpp  |   4 +-
 exemple-graphisme2.cpp  |   4 +-
 exemple-graphisme3.cpp  |  16 +++---
 fibonacci.cpp           |   4 +-
 index.md                | 106 ++++++++++++++++++++++++++++------------
 max-test-tout-en-un.cpp |   6 +--
 max-test.cpp            |   6 +--
 premier-dessin.cpp      |  21 ++++----
 primitives.cpp          |  28 ++++++-----
 primitives.hpp          |  24 +++++----
 11 files changed, 131 insertions(+), 88 deletions(-)

diff --git a/.gradebook.db b/.gradebook.db
index 61ec976c84dc4e896e73739ea2b26103deed93e1..cac8cd143196eddb6829e1037ec99c56320a98fe 100644
GIT binary patch
delta 5049
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
Pu_D7i38Y9kW|{dP6I?3C

delta 3851
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

diff --git a/exemple-graphisme1.cpp b/exemple-graphisme1.cpp
index 745b342..7317429 100644
--- a/exemple-graphisme1.cpp
+++ b/exemple-graphisme1.cpp
@@ -3,8 +3,7 @@ using namespace sf;
 
 #include "primitives.hpp"
 
-int main()
-{
+int main() {
     // Crée une fenêtre de taille 640x480
     RenderWindow window(VideoMode(640, 480), "Ma super fenêtre");
 
@@ -23,4 +22,3 @@ int main()
     return 0;
 }
 
-
diff --git a/exemple-graphisme2.cpp b/exemple-graphisme2.cpp
index 9011afa..4445d3b 100644
--- a/exemple-graphisme2.cpp
+++ b/exemple-graphisme2.cpp
@@ -4,8 +4,7 @@ using namespace sf;
 
 #include "primitives.hpp"
 
-int main()
-{
+int main() {
     /// BEGIN
     // Crée une fenêtre de taille 640x480
     RenderWindow window(VideoMode(640, 480), "Ma super fenêtre");
@@ -33,4 +32,3 @@ int main()
     return 0;
 }
 
-
diff --git a/exemple-graphisme3.cpp b/exemple-graphisme3.cpp
index 6a64196..77124e1 100644
--- a/exemple-graphisme3.cpp
+++ b/exemple-graphisme3.cpp
@@ -4,27 +4,27 @@ using namespace sf;
 
 #include "primitives.hpp"
 
-int main(){
-	// Créé et affiche la fenêtre
+int main() {
+    // Créé et affiche la fenêtre
     RenderWindow window(VideoMode(640, 480), "Ma super fenêtre");
     window.clear(Color::White);
 
-	// Dessine des cercles, ellipses, rectangles, lignes et points
+    // Dessine des cercles, ellipses, rectangles, lignes et points
     draw_circle(window, {20, 20}, 10, Color::Blue);
     draw_circle(window, {50, 20}, 10, Color::Red);
-	draw_rectangle(window, {10, 50}, 40, 20, Color::Blue );
+    draw_rectangle(window, {10, 50}, 40, 20, Color::Blue );
     draw_line(window, {120, 50}, {160, 90}, Color::Black);
     draw_point(window, {10, 110}, Color::Red);
 
-	// Affiche du texte
+    // Affiche du texte
     draw_text(window, {10, 120}, 14, "Voici un exemple de texte.", Color::Black);
 
-	// Met à jour l'affichage.
+    // Met à jour l'affichage.
     window.display();
 
-	// Attend 10 secondes avant la fin du programme.
+    // Attend 10 secondes avant la fin du programme.
     sleep(seconds(10));
 
-	return 0;
+    return 0;
 }
 
diff --git a/fibonacci.cpp b/fibonacci.cpp
index 850813f..0e5b84a 100644
--- a/fibonacci.cpp
+++ b/fibonacci.cpp
@@ -36,13 +36,13 @@ void fibonacciTest() {
     CHECK( fibonacci(6) == 8 );
 }
 
-int main(){
+int main() {
     fibonacciTest();
 
     int n;
     cout << "Entrez un entier : " << endl;
     cin >> n;
-    cout << "Fibonacci(" << n << ") vaut "<< fibonacci(n) << endl;
+    cout << "Fibonacci(" << n << ") vaut " << fibonacci(n) << endl;
     return 0;
 }
 
diff --git a/index.md b/index.md
index 33684a9..246d5a3 100644
--- a/index.md
+++ b/index.md
@@ -38,9 +38,9 @@ en plusieurs fichiers. Puis nous étudierons quelques exemples de
 programmes utilisant la bibliothèque SFML et nous en inspirerons pour
 programmer des dessins et interagir avec l'utilisateur.
 
-+++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "1944ac8026cc545a57bcc39a8d55fe63", "grade": false, "grade_id": "cell-6d952c4decd8c706", "locked": true, "schema_version": 3, "solution": false, "task": false}}
++++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "66602a703f36c2f7e318dabbea427a84", "grade": false, "grade_id": "cell-6d952c4decd8c706", "locked": true, "schema_version": 3, "solution": false, "task": false}}
 
-## <a name=Cours>Cours</a>
+## [Cours](cours.md)
 
 - Cours : [Cycle de vie d'un programme](cours-cycle-de-vie-d-un-programme.md)
 - Cours : [Modularité et compilation séparée](cours-modularite.md)
@@ -55,9 +55,9 @@ programmer des dessins et interagir avec l'utilisateur.
 
 ## TP
 
-+++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "b3fecc2d15e9c0ee06eddcefbb2ad19a", "grade": false, "grade_id": "cell-88edc2f254ba7294", "locked": true, "schema_version": 3, "solution": false}}
++++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "c14862eeb15c1d00c45129ecb04ba658", "grade": false, "grade_id": "cell-88edc2f254ba7294", "locked": true, "schema_version": 3, "solution": false}}
 
-### Exercice : préliminaires : compilation séparée
+### Exercice 1 : préliminaires : compilation séparée
 
 1.  Consultez le contenu des fichiers suivants : [factorielle.hpp](factorielle.hpp),
     [factorielle.cpp](factorielle.cpp), [factorielle-exemple.cpp](factorielle-exemple.cpp).
@@ -106,9 +106,9 @@ programmer des dessins et interagir avec l'utilisateur.
 
 % REMPLACEZ CETTE LIGNE PAR VOTRE RÉPONSE
 
-+++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "f49d0ae7077b8d73d8bb2d37e0d9b548", "grade": false, "grade_id": "cell-41bec4fdd73fc56d", "locked": true, "schema_version": 3, "solution": false}}
++++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "08916a6087c9fab33a500eaeb42a3e19", "grade": false, "grade_id": "cell-41bec4fdd73fc56d", "locked": true, "schema_version": 3, "solution": false}}
 
-### Exercice : compilation séparée
+### Exercice 2 : compilation séparée
 
 +++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "0aa22e643a0731f21a052d645349ad26", "grade": false, "grade_id": "cell-89f33130471fc08e", "locked": true, "schema_version": 3, "solution": false}}
 
@@ -160,38 +160,80 @@ programmer des dessins et interagir avec l'utilisateur.
 
 % REMPLACEZ CETTE LIGNE PAR VOTRE RÉPONSE
 
-+++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "7c740bf5fd075394305a0f98d1dd0422", "grade": false, "grade_id": "cell-96bafcc3c30ace6e", "locked": true, "schema_version": 3, "solution": false}}
++++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "36737f42fbf6300e344e76a3b1301b30", "grade": false, "grade_id": "cell-96bafcc3c30ace6e", "locked": true, "schema_version": 3, "solution": false}}
 
-### Exercice : Premiers graphiques avec Jupyter
+### Exercice 3 : Premiers graphiques avec Jupyter
 
 1.  Refaites l'exercice 2 du [TD](TD.md) en complétant la feuille
     [premier-dessin](premier-dessin.md). Implantez chacun des items
     en vérifiant à chaque fois le résultat.
 
-+++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "e883c664c0706a830228145c1004b74a", "grade": false, "grade_id": "cell-96bafcc3c30ace6f", "locked": true, "schema_version": 3, "solution": false}}
++++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "a57556751a58812ad6c4403b1ef85a5d", "grade": false, "grade_id": "cell-96bafcc3c30ace6f", "locked": true, "schema_version": 3, "solution": false}}
 
-### Exercice : Premiers graphiques avec SFML ♣
+### Exercice 4 : Premiers graphiques avec SFML
 
-:::{attention}
++++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "dc9004964871c8e7a51c6e61d4995bb1", "grade": false, "grade_id": "cell-96bafcc3c30ace6g", "locked": true, "schema_version": 3, "solution": false}}
 
-Il n'est à l'heure actuelle pas encore possible d'utiliser le serveur
-JupyterHub pour cet exercice. Il est donc à effectuer en local en
-salle de TP (ou sur votre machine personnelle si vous avez les
-logiciels requis). Si vous n'avez pas eu le temps de le faire en
-séance de TP, ce n'est pas critique pour l'avancement de votre
-projet. Aussi est-il marqué d'un ♣.
+:::::{attention} SFML et sessions graphiques
 
+Pour exécuter un programme utilisant la bibliothèque SFML, il est
+nécessaire d'être dans une ***interface graphique***; sinon, vous
+aurez une erreur comme :
+```
+./exemple-graphisme1 
+Failed to open X11 display; make sure the DISPLAY environment variable is set correctly
+```
+(X11 est le gestionnaire de session graphique sous UNIX).
+
+::::{admonition} Option 1 : Sur les ordinateurs des salles de TP
+
+Vous êtes automatiquement dans une session graphique. Vous n'avez donc
+rien de particulier à faire.
+
+::::
+
+::::{admonition} Option 2 : Avec le service JupyterHub@Paris-Saclay, depuis tout autre ordinateur ou tablette connectée à Internet
+:class: dropdown
+
+En parallèle de l'interface usuelle JupyterLab, vous pouvez lancer une
+interface graphique (dite XFCE Desktop) sur le même serveur. Vous
+pourrez ensuite basculer entre l'une et l'autre pour éditer et
+compiler vos programmes d'une part, et les exécuter d'autre part.
+
+:::{figure} https://nicolas.thiery.name/Enseignement/Info111/media/screencast-sfml-on-jupyterhub.gif
+Vidéo: lancer un programme graphique sur JupyterHub
 :::
 
-Pour installer les logiciels requis sur votre machine personnelle,
-voir les sections « Aide à l'installation » sur la [page logiciels du
-site du cours](http://nicolas.thiery.name/Enseignement/Info111/logiciels/#aide-installation).
-À noter que, pour utiliser la SFML sur CodeBlocks ou tout autre
-environnement de développement intégré (IDE),
-il faut configurer l'IDE. Plus encore que d'habitude, nous
-recommandons de compiler en ligne de commande dans le terminal. Pour
-l'instant, en salle de TP, il est nécessaire d'utiliser `info-111 compile`
-pour compiler un programme utilisant SFML.
+::::
+
+::::{admonition} Sur votre machine personnelle
+:class: dropdown
+
+L'installation, la configuration et l'utilisation de la SFML peut être
+un peu technique selon la configuration de votre ordinateur (système
+d'exploitation, environnement de développement, ...).  Nous donnons
+ici quelques pointeurs, mais après c'est à vous de fouiller la
+documentation, notamment sur le [site de la
+SFML](https://www.sfml-dev.org/index-fr.php) et de vous
+débrouiller. Dans le doute, utilisez plutôt une des deux options
+ci-dessus.
+
+La section « Salle de TP virtuelle » sur la [page logiciels du site du
+cours](https://nicolas.thiery.name/Enseignement/Info111/ComputerLab/README.html)
+donne des instructions pour installer JupyterLab, compilateur C++ et
+SFML. Sous GNU/Linux (Ubuntu, ...) cela devrait être suffisant pour
+travailler.
+
+Si vous souhaitez utiliser CodeBlocks, Visual Studio, ou tout autre
+environnement de développement intégré (IDE), il faudra configurer cet
+IDE pour utiliser la SFML. Plus encore que d'habitude, nous
+recommandons de compiler en ligne de commande dans le terminal.
+
+::::
+
+:::::
+
++++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "a0ff90a81efe68e4975687391da8ddc6", "grade": false, "grade_id": "cell-96bafcc3c30ace6h", "locked": true, "schema_version": 3, "solution": false}}
 
 1.  Ouvrez les fichiers [exemple-graphisme1.cpp](exemple-graphisme1.cpp)
     et [primitives.hpp](primitives.hpp) et consultez le premier.
@@ -223,9 +265,9 @@ pour compiler un programme utilisant SFML.
     à changer la valeur de la variable `delai` pour voir le résultat
     s'afficher plus longtemps.
 
-+++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "851f78b272e43edba563532592fc2279", "grade": false, "grade_id": "cell-eaefcdbc75785adb", "locked": true, "schema_version": 3, "solution": false}}
++++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "cb4372b6a345f17e803ae8856124785f", "grade": false, "grade_id": "cell-eaefcdbc75785adb", "locked": true, "schema_version": 3, "solution": false}}
 
-### Exercice ♣ : Souris et clavier
+### Exercice 5 : Souris et clavier ♣
 
 1.  Pour vous donner une idée de l'utilisation de la SFML et
     de notre bibliothèque de primitives, lisez attentivement
@@ -239,9 +281,9 @@ pour compiler un programme utilisant SFML.
 
     <!-- Solutions dans le TD !-->
 
-+++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "5917c7b9cfd1dcba875ca0c00faa1060", "grade": false, "grade_id": "cell-5858dbcffa05aed3", "locked": true, "schema_version": 3, "solution": false}}
++++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "6bb1aa5b00f1758bbd44d55f54bc3ea6", "grade": false, "grade_id": "cell-5858dbcffa05aed3", "locked": true, "schema_version": 3, "solution": false}}
 
-### Exercice ♣: Couche d'abstraction
+### Exercice 6 : Couche d'abstraction ♣
 
 Pour vous approprier la couche d'abstraction, consultez son
 implantation dans `primitives.cpp`. En vous inspirant de
@@ -254,9 +296,9 @@ Vous pouvez vous aider de la
 
 % REMPLACEZ CETTE LIGNE PAR VOTRE RÉPONSE
 
-+++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "346c0470eb0efbaa0d4446698ec913d5", "grade": false, "grade_id": "cell-48f3d0b08106b27e", "locked": true, "schema_version": 3, "solution": false, "task": false}}
++++ {"deletable": false, "editable": false, "nbgrader": {"cell_type": "markdown", "checksum": "d07cbe5dd5f6b305779f0b8a6d1128f3", "grade": false, "grade_id": "cell-48f3d0b08106b27e", "locked": true, "schema_version": 3, "solution": false, "task": false}}
 
-### Exercice  ♣: Jeu du Yams
+### Exercice 7_: Jeu du Yams ♣
 
 Reprenez le jeu du Yams du TP 6 en ajoutant une interface graphique.
 
diff --git a/max-test-tout-en-un.cpp b/max-test-tout-en-un.cpp
index 1aea445..7c02da1 100644
--- a/max-test-tout-en-un.cpp
+++ b/max-test-tout-en-un.cpp
@@ -12,9 +12,9 @@ int monMax(int a, int b) {
 }
 
 void monMaxTest() {
-    CHECK( monMax(2,3) == 3 );
-    CHECK( monMax(5,2) == 5 );
-    CHECK( monMax(1,1) == 1 );
+    CHECK( monMax(2, 3) == 3 );
+    CHECK( monMax(5, 2) == 5 );
+    CHECK( monMax(1, 1) == 1 );
 }
 
 int main() {
diff --git a/max-test.cpp b/max-test.cpp
index 5151d81..c0e16e1 100644
--- a/max-test.cpp
+++ b/max-test.cpp
@@ -7,9 +7,9 @@ using namespace std;
 #include "max.hpp"
 
 void monMaxTest() {
-    CHECK( monMax(2,3) == 3 );
-    CHECK( monMax(5,2) == 5 );
-    CHECK( monMax(1,1) == 1 );
+    CHECK( monMax(2, 3) == 3 );
+    CHECK( monMax(5, 2) == 5 );
+    CHECK( monMax(1, 1) == 1 );
 }
 
 int main() {
diff --git a/premier-dessin.cpp b/premier-dessin.cpp
index 71c1de6..95ffdbc 100644
--- a/premier-dessin.cpp
+++ b/premier-dessin.cpp
@@ -5,8 +5,7 @@
 using namespace sf;
 using namespace std;  // Pour les exceptions dans la version distribuée
 
-int main()
-{
+int main() {
     int delai = 1;
     RenderWindow window(VideoMode(900, 480), "Ma super fenêtre");
     window.clear(Color::Green);
@@ -14,7 +13,7 @@ int main()
     // Dessine un point noir de coordonnées (418, 143)
 
     // Remplacez cette ligne et la suivante par le code adéquat
-    throw runtime_error("code non implanté ligne 17");
+    throw runtime_error("code non implanté ligne 16");
 
 
     window.display();
@@ -23,7 +22,7 @@ int main()
     // Dessine un segment blanc entre les points (100,200) et (200,200)
 
     // Remplacez cette ligne et la suivante par le code adéquat
-    throw runtime_error("code non implanté ligne 25");
+    throw runtime_error("code non implanté ligne 24");
 
 
     window.display();
@@ -32,7 +31,7 @@ int main()
     // Dessine un segment rouge entre les points (200,300) et (200,400)
 
     // Remplacez cette ligne et la suivante par le code adéquat
-    throw runtime_error("code non implanté ligne 33");
+    throw runtime_error("code non implanté ligne 32");
 
 
     window.display();
@@ -41,7 +40,7 @@ int main()
     // Dessine un rectangle horizontal vide de sommets diagonaux (200,200) et (400,300) et de contour rouge
 
     // Remplacez cette ligne et la suivante par le code adéquat
-    throw runtime_error("code non implanté ligne 41");
+    throw runtime_error("code non implanté ligne 40");
 
 
     window.display();
@@ -50,7 +49,7 @@ int main()
     // Dessine un rectangle horizontal plein noir de sommets diagonaux (400,150) et (500,200)
 
     // Remplacez cette ligne et la suivante par le code adéquat
-    throw runtime_error("code non implanté ligne 49");
+    throw runtime_error("code non implanté ligne 48");
 
 
     window.display();
@@ -59,7 +58,7 @@ int main()
     // Dessine un segment rouge entre les points (400,300) et (500,400)
 
     // Remplacez cette ligne et la suivante par le code adéquat
-    throw runtime_error("code non implanté ligne 57");
+    throw runtime_error("code non implanté ligne 56");
 
 
     window.display();
@@ -68,7 +67,7 @@ int main()
     // Dessine un cercle noir de centre (415,145) et de rayon 10
 
     // Remplacez cette ligne et la suivante par le code adéquat
-    throw runtime_error("code non implanté ligne 65");
+    throw runtime_error("code non implanté ligne 64");
 
 
     window.display();
@@ -77,11 +76,11 @@ int main()
     // Dessine un disque jaune de centre (700, 100) et de rayon 50
 
     // Remplacez cette ligne et la suivante par le code adéquat
-    throw runtime_error("code non implanté ligne 73");
+    throw runtime_error("code non implanté ligne 72");
 
 
     window.display();
-    sleep(seconds(10*delai));
+    sleep(seconds(10 * delai));
     return 0;
 }
 
diff --git a/primitives.cpp b/primitives.cpp
index 32da2fd..6e127cd 100644
--- a/primitives.cpp
+++ b/primitives.cpp
@@ -3,17 +3,17 @@
 #include <cmath>
 #include <iostream>
 
-void draw_point(RenderWindow& w, Point pos, Color color) {
+void draw_point(RenderWindow &w, Point pos, Color color) {
     Vertex p[] = { Vertex(pos, color) };
     w.draw(p, 1, sf::Points);
 }
 
-void draw_line(RenderWindow& w, Point pos1, Point pos2, Color color) {
+void draw_line(RenderWindow &w, Point pos1, Point pos2, Color color) {
     Vertex p[] = { Vertex(pos1, color), Vertex(pos2, color) };
     w.draw(p, 2, sf::Lines);
 }
 
-void draw_circle(RenderWindow& w, Point center, int r, Color color) {
+void draw_circle(RenderWindow &w, Point center, int r, Color color) {
     CircleShape shape(r);
     shape.setPosition(center);
     shape.setOutlineThickness(1.f);
@@ -23,7 +23,7 @@ void draw_circle(RenderWindow& w, Point center, int r, Color color) {
 }
 
 /// BEGIN draw_filled_circle
-void draw_filled_circle(RenderWindow& w, Point center, int r, Color color) {
+void draw_filled_circle(RenderWindow &w, Point center, int r, Color color) {
     CircleShape shape(r);
     shape.setPosition(center);
     shape.setOutlineThickness(0.f);
@@ -32,7 +32,8 @@ void draw_filled_circle(RenderWindow& w, Point center, int r, Color color) {
 }
 /// END draw_filled_circle
 
-void draw_rectangle(RenderWindow& w, Point pos, int width, int height, Color color) {
+void draw_rectangle(RenderWindow &w, Point pos, int width, int height,
+                    Color color) {
     RectangleShape shape(Vector2f(width, height));
     shape.setPosition(pos); //Top-left position
     shape.setOutlineThickness(1.f);
@@ -41,10 +42,11 @@ void draw_rectangle(RenderWindow& w, Point pos, int width, int height, Color col
     w.draw(shape);
 }
 
-void draw_filled_rectangle(RenderWindow& w, Point pos, int width, int height, Color color) {
+void draw_filled_rectangle(RenderWindow &w, Point pos, int width, int height,
+                           Color color) {
 
     // Remplacez cette ligne et la suivante par le code adéquat
-    throw runtime_error("code non implanté ligne 47");
+    throw runtime_error("code non implanté ligne 49");
 
 }
 
@@ -69,7 +71,7 @@ bool font_is_available() {
     return available;
 }
 
-void draw_text(RenderWindow& w, Point pos, int size, string str, Color co) {
+void draw_text(RenderWindow &w, Point pos, int size, string str, Color co) {
     if ( font_is_available() ) {
         sf::Text text;
         text.setFont(minipax);
@@ -81,18 +83,18 @@ void draw_text(RenderWindow& w, Point pos, int size, string str, Color co) {
     }
 }
 
-Point wait_mouse(RenderWindow& w) {
+Point wait_mouse(RenderWindow &w) {
     Event e;
     do w.waitEvent(e);
-    while(e.type != Event::MouseButtonPressed or
-            e.mouseButton.button != Mouse::Button::Left);
+    while (e.type != Event::MouseButtonPressed or
+           e.mouseButton.button != Mouse::Button::Left);
     return Point(e.mouseButton.x, e.mouseButton.y);
 }
 
-Event::KeyEvent wait_keyboard(RenderWindow& w) {
+Event::KeyEvent wait_keyboard(RenderWindow &w) {
     Event e;
     do w.waitEvent(e);
-    while(e.type != Event::KeyPressed);
+    while (e.type != Event::KeyPressed);
     return e.key;
 }
 
diff --git a/primitives.hpp b/primitives.hpp
index c5835b4..195a2e3 100644
--- a/primitives.hpp
+++ b/primitives.hpp
@@ -57,7 +57,7 @@ using Point = Vector2f;
  * @param pos les coordonnées du point
  * @param color la couleur du point
  */
-void draw_point           (RenderWindow& w, Point pos, Color color);
+void draw_point           (RenderWindow &w, Point pos, Color color);
 
 /** Affiche une ligne de couleur entre deux positions données
  * @param w une fenêtre ouverte dans laquelle dessiner
@@ -65,7 +65,8 @@ void draw_point           (RenderWindow& w, Point pos, Color color);
  * @param pos1 les coordonnées du dernier point de la ligne
  * @param color la couleur de la ligne
  */
-void draw_line            (RenderWindow& w, Point pos1, Point pos2, Color color);
+void draw_line            (RenderWindow &w, Point pos1, Point pos2,
+                           Color color);
 
 /** Affiche un rectangle coloré vide
  * @param w une fenêtre ouverte dans laquelle dessiner
@@ -74,7 +75,8 @@ void draw_line            (RenderWindow& w, Point pos1, Point pos2, Color color)
  * @param height la hauteur du rectangle
  * @param color la couleur du trait
  */
-void draw_rectangle       (RenderWindow& w, Point pos, int width, int height, Color color);
+void draw_rectangle       (RenderWindow &w, Point pos, int width, int height,
+                           Color color);
 
 /** Affiche un rectangle coloré plein
  * @param w une fenêtre ouverte dans laquelle dessiner
@@ -83,7 +85,8 @@ void draw_rectangle       (RenderWindow& w, Point pos, int width, int height, Co
  * @param height la hauteur du rectangle
  * @param color la couleur du trait et du remplissage
  */
-void draw_filled_rectangle(RenderWindow& w, Point pos, int width, int height, Color color);
+void draw_filled_rectangle(RenderWindow &w, Point pos, int width, int height,
+                           Color color);
 
 /** Affiche un cercle coloré vide
  * @param w une fenêtre ouverte dans laquelle dessiner
@@ -91,15 +94,15 @@ void draw_filled_rectangle(RenderWindow& w, Point pos, int width, int height, Co
  * @param r le rayon du cercle
  * @param color la couleur du trait
  */
-void draw_circle          (RenderWindow& w, Point center, int r, Color color);
+void draw_circle          (RenderWindow &w, Point center, int r, Color color);
 
-/** Affiche un cercle coloré plein 
+/** Affiche un cercle coloré plein
  * @param w une fenêtre ouverte dans laquelle dessiner
  * @param center la position du centre du cercle
  * @param r le rayon du cercle
  * @param color la couleur du trait et du remplissage
  */
-void draw_filled_circle   (RenderWindow& w, Point center, int r, Color color);
+void draw_filled_circle   (RenderWindow &w, Point center, int r, Color color);
 
 /** Affiche du texte coloré à une position donnée
  * @param w une fenêtre ouverte dans laquelle dessiner
@@ -108,13 +111,14 @@ void draw_filled_circle   (RenderWindow& w, Point center, int r, Color color);
  * @param str la chaîne de caractères à afficher
  * @param color la couleur du texte
  */
-void draw_text            (RenderWindow& w, Point pos, int size, string str, Color color);
+void draw_text            (RenderWindow &w, Point pos, int size, string str,
+                           Color color);
 
 /** Attend que l'utilisateur appuie sur le clic gauche de la souris
  * @param w la fenêtre dans laquelle l'utilisateur doit cliquer
  * @return la position dans la fenêtre où l'utilisateur a cliqué
  */
-Point wait_mouse(RenderWindow& w);
+Point wait_mouse(RenderWindow &w);
 
 /** Attend que l'utilisateur appuie sur une touche du clavier
  * @param w la fenêtre concernée
@@ -122,6 +126,6 @@ Point wait_mouse(RenderWindow& w);
  * La liste des codes de touches est accessibles ici:
  * https://huit.re/sfml_key_codes
  */
-Event::KeyEvent wait_keyboard(RenderWindow& w);
+Event::KeyEvent wait_keyboard(RenderWindow &w);
 
 #endif
-- 
GitLab