The Rubik`s Project Mémoire Final
Transcription
The Rubik`s Project Mémoire Final
The Rubik's Project Mémoire Final John ALIX Romain ARCILA Matthieu DEVERT Fabien JORQUERA Chargé de TD : M. DESBARATS Client : M. MONTASSIER 6 avril 2007 Résumé du Projet Le Rubik's Cube est un des casse-têtes les plus populaires au monde, mais il n'en demeure pas moins l'un des plus compliqués ! Ainsi, pour permettre à tous de pouvoir s'amuser, mais aussi de comprendre et résoudre son Rubik's Cube, nous devons mettre au point un logiciel en suivant trois axes majeurs. Le premier axe du projet consiste à développer un logiciel possédant une interface graphique ergonomique et conviviale, permettant à un utilisateur de manipuler une représentation en 3 dimensions d'un Rubik's Cube. Pour le deuxième axe de développement, il s'agit d'intégrer au logiciel une interface de numérisation d'un Rubik's Cube physique vers le Rubik's Cube du logiciel. Ainsi, l'utilisateur pourra manipuler et résoudre son Rubik's Cube par l'entremise du logiciel. Troisième et dernier axe, l'intégration au logiciel d'une aide à la résolution d'un Rubik's Cube, i.e. obtenir le(s) prochain(s) mouvement(s) permettant de résoudre le casse-tête. Une extension possible au projet serait de construire un robot (en Lego par exemple) qui résoudrait un Rubik's Cube en le numérisant et en utilisant l'algorithme de résolution du logiciel. Plus précisément, l'intérêt de cette partie réside dans la possibilité de rechercher et de développer les moyens d'interactions possibles entre un ordinateur et du matériel divers. 1 Summary of the Project The Rubik's Cube is one of the most popular brain teaser, but it is also one of the most complicated. In order to help others to entertain with the Rubik's Cube and to solve it, we will have to create a software following three axis. The rst axis consists in developing a graphical interface with focus on usuability ; that makes possible for the user to manipulate a Rubik's Cube in 3 dimensions. The second axis adds an acquisition interface of the Rubik's Cube. This will allow users to solve their own Rubik's Cube with our software. The last axis gives users the possiblity to solve a Rubik's Cube, either step by step, or in one straigth. Building a robot able to solve a Rubik's Cube is one of the possible extension of the software. This robot would do a numerical capture of the Rubik's Cube and would use our sofware to solve it. The main goal of this extension is to allow us to understand how hardwares interact between each other. 2 Table des matières 1 Introduction au domaine d'application 1.1 1.2 1.3 Le Rubik's Cube . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.1 Description de l'appareil . . . . . . . . . . . . . . . . . . . 1.1.2 Un peu d'histoire . . . . . . . . . . . . . . . . . . . . . . . 1.1.3 Les standards . . . . . . . . . . . . . . . . . . . . . . . . . Les algorithmes de résolution . . . . . . . . . . . . . . . . . . . . Mathématiques et Rubik's Cube . . . . . . . . . . . . . . . . . . 1.3.1 Rubik's Cube et théorie des groupes . . . . . . . . . . . . 1.3.2 Nombre de positions diérentes . . . . . . . . . . . . . . . 1.3.3 Nombre de mouvements nécessaires pour revenir en conguration initiale . . . . . . . . . . . . . . . . . . . . . . . 2 Analyse de l'existant 2.1 2.2 2.3 Les logiciels . . . . . . . . . . . . . . . . . . . . . . 2.1.1 Les programmes complets . . . . . . . . . . 2.1.2 Les applets . . . . . . . . . . . . . . . . . . Acquisition d'images : logiciels, bibliothèques... . . 2.2.1 Les bibliothèques C/C++ . . . . . . . . . . 2.2.2 Les bibliothèques Java . . . . . . . . . . . . 2.2.3 La capture grâce à une application en ash Analyse d'image . . . . . . . . . . . . . . . . . . . 3 Introduction au projet 3.1 3.2 3.3 3.4 3.5 But . . . . . . . . . . . . . . . . . . Justication et priorité . . . . . . . . Démarche . . . . . . . . . . . . . . . Schéma de structure envisagé . . . . Schéma de fonctionnement envisagé . 4.1 4.2 4.3 4.4 4.5 4.6 4.7 Relatifs Relatifs Relatifs Relatifs Relatifs Relatifs Relatifs à à à à à à à . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . l'interface et à la modélisation . la manipulation . . . . . . . . . l'acquisition . . . . . . . . . . . la validité des données fournies la résolution . . . . . . . . . . . l'implémentation . . . . . . . . l'installation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 . . . . . . . . . . . . . . . . . . 4 Besoins non fonctionnels . . . . . . . . . . . . . 6 6 6 8 8 10 13 13 13 14 15 15 15 18 19 19 20 20 21 22 22 22 22 23 23 24 24 25 25 26 26 26 27 5 Besoins fonctionnels 5.1 5.2 5.3 5.4 Relatifs Relatifs Relatifs Relatifs à à à à l'interface et à la modélisation . la manipulation . . . . . . . . . l'acquisition et à l'initialisation la résolution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 28 28 29 29 6 Fonctionnalités implémentées et exemples de fonctionnement 31 6.1 6.2 6.3 Interface et visualisation . . . . . . . . . . . . . . . . . . . . . . . Manipulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Résolution et contrôle de validité . . . . . . . . . . . . . . . . . . 7 Comparaison : Prévisions / Réalisations 7.1 7.2 7.3 Comparaison : Cahier des charges / Travail réalisé . . . . 7.1.1 Fonctionnalités obligatoires . . . . . . . . . . . . . 7.1.2 Fonctionnalités optionnelles . . . . . . . . . . . . . 7.1.3 Fonctionnalités supplémentaires . . . . . . . . . . . Comparaison Planning prévisionnel/Planning eectif . . . Comparaison : Architecture prévue / Architecture réalisée 8 Choix 8.1 8.2 8.3 8.4 Choix du langage . Choix de la boite à Bilbiothèque 3D . Choix des outils . . . . . . . . . . . . outils graphique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 Description des algorithmes et structures de données 9.1 9.2 9.3 9.4 9.5 Structure . . . . . . . . . . . . . 9.1.1 Description du module . . 9.1.2 Points forts, points faibles 9.1.3 Problèmes rencontrés . . Acquisition/Traitement . . . . . 9.2.1 Description du module . . 9.2.2 Points forts, Points faibles 9.2.3 Problèmes rencontrés . . Modèle 3D . . . . . . . . . . . . 9.3.1 Description du module . . 9.3.2 Points forts, points faibles 9.3.3 Problèmes rencontrés . . Interface . . . . . . . . . . . . . . 9.4.1 Description du module . . 9.4.2 Points forts, points faibles 9.4.3 Problèmes rencontrés . . Résolution . . . . . . . . . . . . . 9.5.1 Description du module . . 9.5.2 Points forts, points faibles 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 32 32 33 33 33 33 34 35 36 37 37 37 38 38 40 40 40 42 43 43 43 44 45 45 45 47 47 48 48 49 50 50 50 51 10 Tests de validation et de fonctionnements 10.1 Tests aléatoires . . . . . . . . . . . . . . . 10.2 Tests unitaires . . . . . . . . . . . . . . . 10.2.1 Module Structure . . . . . . . . . . 10.2.2 Module Résolution . . . . . . . . . 10.2.3 Module Acquisition . . . . . . . . . 10.2.4 Module Rubik . . . . . . . . . . . 10.3 Tests d'intégration . . . . . . . . . . . . . 10.3.1 Substitutivité . . . . . . . . . . . . 10.4 Tests système . . . . . . . . . . . . . . . . 10.4.1 Mouvements d'une face du Rubik's 10.4.2 Acquisition d'un Rubik's Cube . . 10.4.3 Résolution d'un Rubik's Cube . . . 10.5 Tests en boîte noire . . . . . . . . . . 10.5.1 Questionnaire . . . . . . . . . . . . 10.5.2 Essais d'homologation . . . . . . . 10.6 Tests de fonctionnement . . . . . . . . . . 10.7 Tests en boîte blanche . . . . . . . . . 10.7.1 Tests mémoire . . . . . . . . . . . 11 Extensions et améliorations possibles 11.1 Extensions . . . . . . . . . 11.1.1 Acquisition . . . . 11.1.2 Résolution . . . . . 11.2 Améliorations . . . . . . . 11.2.1 Acquisition . . . . 11.2.2 Visualisation 3D . 11.2.3 Interface graphique 11.2.4 Divers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Cube . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 52 52 52 54 55 55 56 56 57 57 57 61 62 62 63 64 64 64 65 65 65 65 66 66 66 66 67 Annexes 70 A Questionnaire 70 B Informations insolites sur le Rubik's Cube 72 C Howto 74 D Valgrind 77 C.1 Chargement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.2 Compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C.3 Utilisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les mots écrits en italique sont présents dans le glossaire 5 74 74 75 Chapitre 1 Introduction au domaine d'application 1.1 Le Rubik's Cube Bien que vous ayez certainement déjà eu l'occasion de manipuler un Rubik's Cube, nous allons maintenant vous présenter brièvement et sous des aspects moins communs cet objet. 1.1.1 Description de l'appareil Le Rubik's Cube est comme son nom l'indique un cube formé d'un assemblage de 26 cubes plus petits : les cubics. Chaque face du cube est composée de 9 cubics. Le Rubik's Cube est un cube 3x3x3 (trois rangées de trois cubics par face), mais il existe de nombreuses variantes : Pocket Cube (2x2x2), Rubik's Revenge (4x4x4), Professor's Cube (5x5x5), mais aussi d'autres formes géométriques (tétraèdre, octaèdre...). Fig. 1.1 Un Rubik's Cube résolu. Crédit : [Wik07a] Chaque face pivote indépendamment par rapport aux autres grâce à un mécanisme interne assez complexe composé notamment d'un axe central reliant 6 les cubics centraux de chaque face. Fig. 1.2 Un Rubik's Cube désassemblé. Crédit : [Wik07a] Dans l'état résolu d'un Rubik's Cube, chaque face est d'une seule couleur. Il y a 6 couleurs diérentes au total : le blanc, le bleu, le vert, l'orange, le rouge et le jaune. Grâce aux rotations des faces, les couleurs du Rubik's Cube se mélangent. Le jeu consiste alors à réunier la couleur de chaque face. Fig. 1.3 Un Rubik's Cube mélangé et en cours de rotation. Crédit : [Wik07a] Il y a 3 types de cubics : les cubics centraux, les cubics d'arêtes et les cubics de coins. Les 6 cubics centraux sont bien évidemment chacun d'une couleur différente et surtout, propriété importante : ils sont invariants les uns par rapport aux autres. Par exemple, sur un cube standard le cubic central blanc sera toujours opposé au jaune , le orange au rouge et le bleu eu vert. De ce fait, les 6 cubics centraux forment un repère invariant. Les 12 cubics d'arêtes sont colorés de 2 couleurs diérentes sur leur 2 faces apparentes. Ils ne peuvent prendre place qu'à côté d'un cubic central selon une cardinalité 4 points. Bien entendu, on en retrouve 4 par face et un tel cubic, de par ses 2 faces apparentes, est présent sur 2 faces du Rubik's Cube. Les 8 cubics de coins sont colorés de 3 couleurs diérentes sur leur 3 faces apparentes. Ils ne peuvent prendre place que sur les coins de chaque face. Là encore il existe 4 spécimens par face et ils chevauchent 3 faces. 7 1.1.2 Un peu d'histoire Le Rubik's Cube a été inventé par Erno Rubik, professeur d'architecture et sculpteur hongrois, en 1974. Fig. 1.4 Portrait d'Erno Rubik. Crédit : [Pic00] La commercialisation dans les magasins de jouets de son pays débute en 1977. La popularité croissante de ce jeu en Hongrie permet au Rubik's Cube d'être vendu dans le monde entier en 1980. En l'espace de deux ans, c'est le raz de marée. En eet, cent millions de Rubik's Cube sont vendus entre 1980 et 1982. Depuis, le Rubik's Cube a gagné le titre de casse-tête parfait, et aujourd'hui plus de deux cent millions d'exemplaires ont été vendus. Durant la période faste du Rubik's Cube, où ce jeu est devenu un véritable phénomène de mode, de nombreux concours de vitesse ont vu le jour. La pratique du Rubik's Cube de vitesse (résoudre un Rubik's Cube le plus rapidement possible) est appelée le SpeedCubing. Vu la complexité apparente du casse-tête, terminer un cube en moins d'une minute apparaît être une grande performance. Pourtant, le record authentié pour résoudre un Rubik's Cube est de 10,48 secondes, eectué par Toby Mao (Californie) en 2006, au concours national des États Unis. D'autres catégories de concours existent : en aveugle, à une main, avec les pieds ou encore en faisant le moins de mouvements possibles. D'après [Wik07b], les règles pour les concours sont les suivantes : tout d'abord, le compétiteur a 15 secondes pour observer le Rubik's Cube puis le remet au juge de la compétition. Ensuite, il pourra le revoir dès qu'il aura déclenché le chronomètre. Le chronomètre s'arrête une fois que le compétiteur à posé le Rubik's Cube sur la table et ses deux mains à plat devant lui. Il existe deux méthodes pour juger les candidats : Le meilleur temps de résolution parmi un certain nombre d'essais. La moyenne des temps de résolution parmi un certain nombre d'essais. 1.1.3 Les standards Il existe quelques désignations standards pour le Rubik's Cube qui sont indispensables pour la compréhension des méthodes de résolutions. Ces standards servent notamment à représenter les faces du cube et le mouvement qu'on doit 8 produire sur celles-ci. Nous utiliserons les standards anglo-saxons qui semblent être les plus répandus (voir [Bum]). Fig. 1.5 Vue des 6 faces d'un Rubik's Cube résolu. Crédit : [Bum] Des noms ont été choisis pour désigner chaque face en fonction de leur orientation par rapport au joueur. On trouve fréquemment les conventions suivantes : La face bleue est appelée face UP, abréviation U ; la face blanche est appelée face FRONT, abréviation F ; la face rouge est appelée face RIGHT, abréviation R ; la face verte est appelée face DOWN, abréviation D ; la face jaune est appelée face BACK, abréviation B ; la face orange est appelée face LEFT, abréviation L. Grâce à ces désignations, on peut facilement décrire un mouvement. En eet, le standard veut que lorsque l'on appelle un mouvement R, cela veut dire tourner la face RIGHT d'un quart de tour dans le sens horaire . Le quart de tour est le mouvement de base du Rubik's Cube. On peut aussi décrire le mouvement inverse, noté alors R', et qui veut dire tourner la face RIGHT d'un quart de tour dans le sens anti-horaire . Fig. 1.6 Application d'un mouvement R'. Crédit : [Bum] Ainsi, en enchaînant U F' B par exemple, on tournera d'abord la face du haut dans le sens horaire, puis la face de devant dans le sens anti-horaire et 9 enn la face arrière dans le sens horaire. Il existe encore une autre notation pour désigner le mouvement des faces du milieu, celles dont on ne voit que les côtés. La face nommée Um est la face située entre la face UP et son opposée (qui est la face DOWN). La face nommée Fm est la face située entre la face FRONT et la face BACK, et la face nommée Rm est la face située entre la face RIGHT et la face LEFT. Fig. 1.7 Application d'un mouvement Um . Crédit : [Bum] Cependant, ces types de mouvements ne sont utilisés que si on ne se préoccupe pas de garder un référentiel particulier (si on peut changer la position d'un centre). C'est pourquoi, on préférera sûrement eectuer un mouvement R' suivis d'un mouvement L plutôt que de faire un mouvement Rm . 1.2 Les algorithmes de résolution Vu le nombre exorbitant de congurations possibles, on ne peut résoudre un Rubik's Cube au hasard, mais rassurez-vous, de nombreuses techniques de résolutions ont vu le jour depuis 1980. Nous allons présenter ici les principales méthodes. Nous allons voir dans la prochaine partie certains algorithmes pouvant orir un nombre dérisoire de mouvements pour résoudre un Rubik's Cube. Cependant, ils ne sont applicables que par des machines car ils demandent de remplir des grandes tables de mouvements impossibles à mémoriser dans le cerveau humain (à moins d'être quelqu'un d'exceptionnel bien entendu). Des algorithmes moins ecaces mais à dimension humaine existent donc pour nous permettre de résoudre notre casse-tête préféré. Voici les méthodes les plus employées, tirées de [Wik07b] et de [Wik07c] ; elles sont pour la plupart utilisées avec plus ou moins d'améliorations, pour le SpeedCubing par exemple : Méthode couche par couche , la plus simple et la plus longue, souvent employée par les débutants : 1. Tout d'abord, il faut réaliser une face entière. 2. Il faut ensuite placer la première couronne, c'est à dire chaque cubic se situant en dessous de la face complétée. En plaçant ensuite 10 convenablement les centres, on obtient ce que nous montre la gure suivante. 1.8 Rubik's Cube avec la première couronne terminée. La face du dessus est complète ainsi que la première couche. Les cubics colorés en gris peuvent être de n'importe quelle couleur. Fig. 3. Il faut ensuite placer la deuxième couronne, c'est à dire positionner correctement les cubics d'arêtes se situant en dessous de la première couche. La gure suivante illustre cette étape. 1.9 Rubik's Cube avec la seconde couronne terminée. La face du dessus, la première couche et la deuxième couche sont complètes. Les cubics colorés en gris peuvent être de n'importe quelle couleur. Fig. 4. Ensuite il faut déplacer les cubics d'arêtes de la dernière couche à leur places et les positionner correctement. 5. Pour nir, il ne reste plus qu'à positionner les cubics de coins de la dernière couche et de les placer au bon endroit. Une deuxième méthode assez simple : 1. Tout d'abord, il faut résoudre une face entière. 2. Ensuite il faut réaliser la face opposée à celle déjà en place. Il faut d'abord mettre les cubics de coins puis ceux d'arêtes. 11 3. Il faut ensuite bien placer tous les centres en tournant la rangée du milieu du Rubik's Cube. Désormais il ne reste plus que les cubics d'arêtes de la couche centrale à placer. 4. Il ne reste plus qu'à placer les cubics d'arêtes de chaque face non réalisée, et de les orienter comme il faut. Troisième algorithme : celui de Lars Petrus 1 , SpeedCuber suédois né en 1960. Il termina quatrième du premier championnat du monde de Rubik's Cube à Budapest en 1982. Son record personnel est à 13,60 secondes. Fan inconditionnel de cette discipline, il parvient toujours à tenir la dragée haute aux adolescents qui dominent traditionnellement les compétitions. Sa méthode est beaucoup plus rapide que les précédentes (60 mouvements en moyenne), mais plus délicate. Vous pouvez aller voir la page traduite de la méthode de résolution de Lars Petrus : [Cou97]. 1. Pour commencer, il faut réaliser un cube 2x2x2 (de 3 couleurs). 2. Ensuite on va avancer par agrandissements successifs de la forme. Il faut passer du petit cube à un parallélépipède rectangle de 2x2x3 (formé par 4 couleurs) sans toucher au cube 2x2x2 créé précédemment. 3. Avant d'agrandir à nouveau la forme, il faut arranger les 2 faces libres de manière à acher une croix sur chacune d'elles. 4. On agrandit encore la forme pour obtenir une forme 2x3x3, on complète ainsi 2 couches du Rubik's Cube. 5. Pour terminer, on place d'abord les 4 cubics de coins restants puis les 4 cubics d'arêtes. Quatrième algorithme, celui de Jessica Fridrich 2 , professeur d'informatique à l'Université de New York. Elle est l'auteur de la méthode de résolution la plus prisée des SpeedCubers. Son algorithme est à peu près aussi rapide que celui de Lars Petrus, mais il est améliorable à certains endroits ; il est donc très apprécié en SpeedCubing. 1. Cet algorithme commence facilement : il sut de réaliser une croix sur une face. 2. Ensuite, il faut placer chaque cubic de coin de cette face tout en plaçant le cubic d'arête qui leur correspond (à savoir les cubics d'arêtes de la rangée du milieu). Ainsi, à cette étape les deux premières couches du Rubik's Cube sont terminées. 3. Pour nir, il faut orienter puis bien positionner les cubics de la dernière face. Il faut savoir que pour des mouvements diciles (comme rétablir les 4 derniers cubics de coins par exemple), des algorithmes de mouvements (appelés patterns ) existent et permettent, par exemple, de permuter un ensemble de 3 cubics sans bouger le reste du Rubik's Cube. Lorsque l'on débute dans la résolution, on peut sans peine réussir à terminer son Rubik's Cube en suivant pas à pas ces méthodes. Il existe trois types de patterns essentiellement utilisés. 1 Lien vers sa page personnelle : http ://lar5.com/ 2 Lien vers sa page personnelle : http ://www.ws.binghamton.edu/fridrich 12 Tout d'abord les opérations dites de cycle de 3 arêtes , qui permutent 3 cubics d'arêtes du Rubik's Cube qui ne sont pas nécessairement sur la même face à l'origine. Il existe 3 suites de mouvements diérents qui peuvent eectuer cette opération, à partir de congurations diérentes du cube. Ensuite, l'opération de cycle de 3 coins , qui permute 3 cubics de coins du Rubik's Cube. Enn, l'opération twist de coins qui permet de faire tourner sur eux mêmes 3 cubics de coins, et eectue en même temps une opération de cycle de 3 arêtes. 1.3 Mathématiques et Rubik's Cube Outre le fait d'être un des jeux les plus répandus au monde, le Rubik's Cube est aussi un objet en forte relation avec les mathématiques. En eet, sa structure et les mouvements qui lui sont associés sont en corrélation avec un objet mathématique fondamental : le groupe. 1.3.1 Rubik's Cube et théorie des groupes Le Rubik's Cube est associé à la théorie des groupes car l'ensemble des manoeuvres de celui-ci forme un groupe. Soit M l'ensemble des manoeuvres du Rubik's Cube, et soit suivi de une opération. Soit m n et o ∈ M . m suivi de n ∈ M (une manoeuvre suivie d'une autre manoeuvre est encore une manoeuvre). Par conséquent, M est stable par l'opération suivi de . (m suivi de n) suivi de o ⇔ m suivi de (n suivi de o). Par conséquent, suivi de est une opération associative. Soit ² la manoeuvre qui consiste à ne rien faire. m suivi de ² ⇔ m (une manoeuvre suivie de ne rien faire est toujours la même manoeuvre). Ainsi, la manoeuvre qui consiste à ne pas bouger est l'élément neutre. Chaque manoeuvre peut être eectuée à l'envers. Ceci est donc une manoeuvre inverse. Voila donc pourquoi l'ensemble des manoeuvres du Rubik's Cube constitue un groupe. Pour prolonger le plaisir de la théorie mathématique, nous vous conseillons [Joy 7]. 1.3.2 Nombre de positions diérentes Voici le calcul du nombre de congurations possibles d'un Rubik's Cube (calcul issu de [Wik07c] et [Pic00]) : Il y a 12 cubics d'arêtes et chacun d'entre eux possède 2 orientations possibles, ce qui donne 212 possibilités. Or, on ne peut modier l'orientation d'un seul cubic sans en bouger un autre. L'orientation du dernier cubic est xée par les autres. Par conséquent on n'a plus que 211 possibilités. Il y a 8 cubics de coins et chacun d'entre eux possède 3 orientations possibles ce qui donne 38 possibilités. Or, on ne peut modier l'orientation 13 d'un seul cubic sans en bouger un autre. L'orientation du dernier cubic est xée par les autres. Il ne reste donc plus que 37 possibilités. Les 12 arêtes peuvent prendre 12 positions diérentes. Si une arête prend une place, la suivante n'aura plus que onze possibilités, etc. Par conséquent, il y a 12! possibilités de positionnement des cubics d'arêtes. Les 8 cubics de coins peuvent prendre 8 positions diérentes. Si un coin prend une place, le suivant n'aura plus que 7 possibilités, etc. Par conséquent, il y a 8! possibilités de positionnement des cubics de coins. Les cubics centraux ne comptent pas car ils sont invariants. Il reste une contrainte : lorsque tous les cubics sont positionnés sauf 2, la position de ces 2 derniers cubics est imposée. En eet, il est impossible de permuter seulement 2 cubics. Par conséquent, il faut diviser le nombre de possibilités par deux. Ce qui nous amène au résultat suivant : 8! × 37 × 12! × 210 = 43252003274489856000 Soit un peu plus de 43 milliards de milliards de possibilités. Il est donc quasiment impossible de résoudre un Rubik's Cube au hasard ; il faudrait plus d'un millénaire à raison de mille mouvements par seconde pour explorer toutes les congurations. 1.3.3 Nombre de mouvements nécessaires pour revenir en conguration initiale Depuis que le Rubik's Cube a vu le jour, le grand dé est de trouver un algorithme qui peut résoudre n'importe quel Rubik's Cube en utilisant le moins de mouvements possibles. Cette question a même fait l'objet de diverses thèses universitaires ! La première personne à faire descendre le nombre maximal de mouvements en dessous de 100 fut le mathématicien anglais Morwen B. Thistlethwaite. Dans sa première mouture, le nombre de mouvements maximal de cet algorithme était de 52 ; ce chire a été ramené par la suite à 42 puis 29 mouvements. Grâce à la théorie des groupes, on a prouvé mathématiquement qu'il existe un algorithme pour résoudre n'importe quel Rubik's Cube en 22 mouvements maximum. Cet algorithme a été baptisé l'algorithme de Dieu (God's Algorithm). Si personne n'a trouvé l'algorithme de Dieu, il existe cependant un algorithme révolutionnaire qui résout un Rubik's Cube en 18 à 20 coups en moyenne. Cet algorithme à été mis au point par Herbert Kociemba (chercheur allemand et fan inconditionnel de Rubik's Cube), en 1992, en améliorant l'algorithme de Morwen B. Thistlethwaite. Cependant, cet algorithme résout des Rubik's Cube faciles en 12 mouvements, mais peut mettre plus de 22 mouvements pour des Rubik's Cube diciles. Comme précisé plus haut, ces algorithmes ne sont pas applicables par un humain car ils se servent de tables de mouvements pour calculer la solution. Ils ne sont donc utilisés que par les ordinateurs. 14 Chapitre 2 Analyse de l'existant La communauté cubiste (les pratiquants de Rubik's Cube) étant très active et l'époque d'apparition du casse tête coïncidant avec l'émergence de l'informatique, il existe sur Internet une masse d'informations et de programmes très conséquente. Toutefois ceci n'est pas qu'un avantage car sélectionner des informations pertinentes dans une telle masse de données n'est pas chose aisée. Au contraire, la littérature sur le Rubik's Cube n'est pas courante. Il existe certes de nombreuses références sur le SpeedCubing mais ceci n'est pas enrichissant pour notre projet. On peut toutefois citer Adventures in Group Theory : Rubik's Cube , Merlin's Machine, and Other Mathematical Toys [Joy02] , Mathematics of the Rubik's Cube [Joy 7] ou encore The Mathematics of the Rubik's Cube [Bum] qui traitent du Rubik's Cube sous un aspect purement mathématique, plus particulièrement de la théorie des groupes soutenant le modèle du Rubik's Cube. An de rester clairs et de bien xer les choses, nous parlerons de l'existant au niveau logiciel pour mieux cerner ce qui se fait. Pour cela nous nous appuierons sur une brève analyse des logiciels les plus populaires en la matière. Dans un autre temps nous entrerons plus en détails sur ce qui existe pour les diérents objectifs de notre projet. 2.1 Les logiciels 2.1.1 Les programmes complets Il existe sur Internet de nombreux logiciels se rapportant au Rubik's Cube qui vont du logiciel professionnel aux réalisations plus amateures voire incongrues. Dans cette partie nous nous proposons d'analyser un panel des logiciels les plus populaires. La sélection a été réalisée selon l'ordre d'apparition de ces diérents programmes sur le robot de recherche Google , mais également par rapport à leur popularité chez les cubistes, sur la foi des sites spécialisés. Nous verrons ensuite quelques applications de moindre importance mais qui par un aspect ou un autre peuvent également nous intéresser. 15 CubeTwister : la référence Le logiciel le plus populaire semble être CubeTwister 1 . Un programme libre et Open Source qui peut traiter le cube classique 3*3*3, objet de notre projet, mais aussi des Rubik's Cube plus étonnants : des cubes de taille variable, la Barrique, le Diamant et l'Octaèdre ! Le programme est paramétrable selon les souhaits de l'utilisateur. En eet celui-ci reconnaît les notications de mouvements selon 5 conventions diérentes. L'utilisateur a même la possibilité de dénir son propre système de notations grâce à un système de scripts extrêmement simple à utiliser. De plus ce programme possède une base de données pour les man÷uvres et les patterns (i.e. une suite de mouvements permettant par exemple de changer seulement l'orientation de deux coins sans changer le reste de la conguration du cube). Là encore, un utilisateur lambda peut dénir ses propres scripts assez facilement. Une fonction de résolution est bien entendu intégrée. L'algorithme utilisé est un des plus ecaces : l'algorithme de Kociemba. Nous sommes donc en face d'un logiciel qui eectue une véritable résolution. L'utilisateur peut entrer une conguration de cube et demander au programme de le résoudre. Nous verrons par la suite que ce n'est en fait que rarement le cas. Ce logiciel fonctionne sur toutes les plates-formes grâce à la machine virtuelle Java. En ce qui concerne les défauts, on peut noter une interface un peu austère. Ceci permet certes une clarté de l'interface mais elle pourra rebuter certaines personnes. De plus la multitude de fonctionnalités présentes, notamment les fonctions de script, peut faire passer ce logiciel pour un programme dicile à utiliser alors que ce n'est pas le cas. Une aide contextuelle aurait peut-être été la bienvenue. Le logiciel n'est pas doté d'un module de capture par Webcam ou autre, la conguration d'un cube doit se faire à la souris. Il n'en demeure pas moins que nous sommes en face d'un excellent logiciel et qui sera à n'en pas douter une source d'inspiration intéressante pour notre future réalisation, même si celle-ci n'a pas l'ambition d'être aussi complète que Cube Twister. Fig. 2.1 Interface du Cube Twister : mise en place des scripts 1 CubeTwister : http ://www.randelshofer.ch/cubetwister2/download.html 16 Cube Explorer 4.0 : le logiciel de M. Kociemba Herbert Kociemba, le père de l'algorithme éponyme, a aussi développé un logiciel 2 traitant du problème de la résolution du Rubik's Cube. Disons le tout de suite, ce logiciel est d'une extrême puissance. Il permet des choses toutes à fait étonnantes à la mesure de la passion de Kociemba pour le Rubik's Cube. On peut, outre les fonctions classiques de modélisation et de résolution, éditer des patterns et les tester grâce à un générateur de cube. On peut également comparer le nombre de coups nécessaires à la résolution selon les patterns détectés dans les Rubik's Cube, chercher des symétries sur le cube an d'utiliser les patterns les plus appropriés à la résolution, etc. Par ailleurs, le cube n'est pas représenté sous une forme 3D classique, comme un cube réel, mais comme un patron aplati du cube, ce qui n'est pas évident à appréhender pour un novice. Ce choix est compréhensible car ce programme est clairement destiné au cubiste possédant de solides connaissances en informatique et en mathématiques. En eet, non seulement la plupart des fonctionnalités oertes par le programme ne concerne pas le cubiste amateur, mais en plus, l'interface est, à notre sens, un exemple de non ergonomie. Menus, sous-menus et réglages divers sont omniprésents. Toutefois il y a quand même quelques points intéressants en ce qui concerne la manipulation du cube. Une ligne de commande destinée à rentrer les mouvements au clavier est présente. Le joueur peut ainsi saisir une séquence de mouvements au lieu d'indiquer ceux-ci un par un. De plus, ce programme est doté d'un système de capture par Webcam comme demandé pour notre projet. Assez complet mais pas évident à paramétrer, il assure par contre, d'après nos tests personnels, une très bonne détection des Webcams. De plus le protocole qui régit la capture d'un Rubik's Cube peut être une piste intéressante pour notre logiciel. En eet pour bien capturer chaque face, et surtout leurs orientations les unes par rapport aux autres, trois mouvements de 90° sont demandés an d'exposer les faces par séries de 2. Ce programme tourne exclusivement sous Windows. Il est malheureusement impossible de trouver le code source. Nous continuons nos recherches car les versions précédentes de Cube Explorer étaient apparemment Open Source. Fig. 2.2 Cube Explorer : simples amateurs, passez votre chemin ! 2 Cube Explorer : http ://kociemba.org/cube.htm 17 2.1.2 Les applets Après avoir vu les deux mastodontes de la résolution de Rubik's Cube nous allons voir une autre catégorie de programmes : les applets Java. Ces applets pullulent littéralement sur Internet. Bien évidemment leur qualité est très variable. Voyons ce qui se fait de plus signicatif dans ce domaine : Tout d'abord, des applets comme celui de Karl Hörnell 3 qui sont les plus basiques. Une modélisation du cube et des contrôles à la souris en cliquant sur la couronne à faire tourner. Il n'y a aucun algorithme de résolution implémenté et la représentation 3D est souvent sommaire. Ceci ne sera pas très intéressant pour nous. Nous signalons simplement leur existence. Bien sûr, il est possible de trouver des applets de bien meilleure qualité. L'Applet Magic, par exemple, est un applet où le cube est assez bien modélisé et où les mouvements se font de manière instinctive. Comme beaucoup de bons applets java sur le Rubik's Cube, nous avons trouvé celui-ci sur le site [Pic00]. Malheureusement, il est impossible de le trouver ailleurs. Nous nous permettons donc de donner ce site comme référence. 2.3 L'Applet Magic : maniable, agréable, mais pas de résolution à proprement parlé Fig. Malheureusement le programme ne permet pas de rentrer sa propre conguration de cube, il propose un puzzle avec un choix dans le niveau de diculté. En eet, ce type de programmes ne résout pas vraiment le cube, ils eectuent simplement une suite de mouvements qu'ils gardent en mémoire. Quand l'utilisateur demande de résoudre le Rubik's Cube, ceux-ci font simplement les mouvements inverses de ceux qu'ils avaient eectués lors du mélange, en ajoutant éventuellement l'inverse de ceux entrés par l'utilisateur. Bien entendu, ce type de programmes n'inclut pas d'acquisition par Webcam non plus. Toutefois la représentation des cubes ou l'ergonomie dans ces programmes sont parfois impressionnantes et pourraient être intéressantes pour notre projet. Enn une dernière catégorie de logiciel un peu moins attrayante, mais qui est toute aussi intéressante pour nous : les solveurs de Rubik's Cube qui ne modélisent pas en 3 dimensions le cube, mais orent résolution et manipulation 3 http ://www.javaonthebrain.com/java/rubik/ 18 en s'exécutant sur une console. On peut citer par exemple le Dimcik's Rubik 4 écrit en Java. En eet ces programmes souvent austères sont souvent très bien codés et Open Source. Il pourrait être intéressant pour nous de voir l'architecture adoptée par les programmeurs ainsi que la (ou les) structure(s) représentant le Rubik's Cube en mémoire. En eet, pour une manipulation facile de la structure, avec un temps d'accès acceptable, le choix de celle-ci doit être particulièrement rééchi. Ainsi, dans le cas du Dimcik's Rubik, on trouve un système de tableau à 3 dimensions qui permet une implémentation des mouvements du Rubik's Cube excessivement concise. 2.2 Acquisition d'images : logiciels, bibliothèques... Dans le domaine de la capture d'image provenant d'une Webcam, l'existant est beaucoup moins vaste que dans le cas des programmes de modélisation de Rubik's Cube, notamment quand on a l'ambition d'orir une application portable et libre de droit. 2.2.1 Les bibliothèques C/C++ On peut citer certaines bibliothèques en C++ comme PWLib 5 qui provient du framework OpenH323 destiné à la vidéo-conférence. Cette bibliothèque sert de fondation pour le framework en fournissant des classes de base pour les string, le multithreading... ainsi que pour l'accès aux périphériques vidéo. Cependant les conteneurs de cette bibliothèque ne sont pas compatibles avec ceux de la STL ; elle est également trop lourde en terme de place et de manipulation par rapport à nos besoins. De plus PWLib n'est pas compatible avec Mac OS X et elle est assez mal documentée. En C++, il existe également OpenWengo 6 : application de Voix par IP : version libre et Open Source de Skype. Dans la nouvelle version (version ng), elle ore une architecture modulaire, il est donc possible de récupérer facilement le code pour la Webcam. OpenWengo est portable sur les trois plate-formes voulues mais, revers de la médaille, il est basé sur un code en cours de développement, d'où des problèmes récurrents de compilation, et il peut toujours y avoir des modications de l'API. Même si nous avons trouvé des choses susceptibles d'être plus intéressantes pour nous, cette application n'est pas à négliger pour l'instant. Autre bibliothèque en C++ : PortVideo 7 est une bibliothèque gérant l'accès au périphériques d'acquisition vidéo : cette application a l'avantage d'être assez légère, totalement portable (win32, Linux, MacOS X, testée sur win32 et Linux), de gérer les caméras sur USB et Firewire et elle est bien entendu Open Source. Quelques petits tests réalisés donnent des résultats encourageants. Enn dernière piste explorée : OpenCV, 8 une bibliothèque C Open Source 4 http ://dimm.ifrance.com/old/dimakubik.html 5 PWLib : http ://www.openh323.org/PWLib 6 OpenWengo : http ://ww.openwengo.org 7 PortVideo : http ://www.iua.upf.es/mtg/reacTable/ ?portvideo 8 OpenCV : http ://www.intel.com/technology/computing/opencv/index.htm 19 en partie développée par Intel. Les possibilités semblent vastes et une version Linux existe également. Toutefois, à l'instar de la PWLib, cette application est peut-être un peu trop dense pour nos besoins même si elle reste une solution envisageable. Elle a cependant comme avantage d'orir certaines fonctions de traitement d'image. Un autre moyen de détecter et d'acher le signal provenant d'une Webcam est l'utilisation du module dédié de aMSN. Cependant, ce module est entièrement écrit en Tool Command Language et la reprise de code s'annonçait fastidieuse de par notre manque de connaissance de ce langage et l'absence totale de commentaire dans le code source. 2.2.2 Les bibliothèques Java En Java, les applications de ce type semblent plus limitées et reposent uniquement sur le Java Media Framework 9 . On peut noter de lourdes dicultés à installer ce framework durant nos tests : erreurs de compilation, de bibliothèques ... De plus l'engouement pour le JMF ne semble pas être énorme dans le monde des développeurs. Toutefois une application comme celle de David Fischer 10 permettant de stocker un ux vidéo et de l'exploiter ensuite, serait pour nous largement susante. Reposant sur Java, elle est censée être totalement portable. Lors d'un rapide essai nous avons pu voir que la réalité n'est pas aussi idyllique. Réussissant tout de même à la faire fonctionner sous Windows, le résultat est alors globalement positif. Le problème est que le programme, tout du moins la partie concernant la caméra USB, doit être conguré par l'utilisateur avant d'être ensuite compilé. Nous ne sommes clairement pas en face d'une solution grand public. Néanmoins, certaines parties du code seront peut-être réutilisables. 2.2.3 La capture grâce à une application en ash Enn, dernière solution trouvée lors de nos recherches : l'utilisation de la technologie Flash. En eet depuis la version 8 de Flash 11 , la classe BitmapData a fait son apparition et permet de manipuler des images. La photo prise est ensuite stockée sur le disque dur grâce à un petit script PHP. Les avantages de l'utilisation de cette technologie sont un repérage immédiat de la quasi-totalité des Webcams du marché, pas seulement les Webcams USB, mais aussi celles qui sont intégrées (comme sur les ordinateurs portables), une portabilité sur les trois systèmes d'exploitation majeurs du marché. De plus, elle dispense de posséder des solutions lourdes à côté pour faire tourner l'application. Un programme comme WebSnap 12 permet de réaliser ceci facilement et est assez bien documenté. Cependant, cela implique l'utilisation de JDIC 13 pour incorporer le code Flash dans une application Java. JDIC est un projet SUN permettant la présence dans une application Java de nombreux outils, avec entre 9 JMF : http ://Java.sun.com/products/java-media/jmf/ 10 http ://www.mutong.com/scher/java/usbcam/ 11 http ://www.adobe.com/fr/products/ash/ashpro/ 12 http ://ww.homeunix.net/category/ash/ 13 https ://jdic.dev.java.net/ 20 autre la possibilité d'incorporer un Browser l est donc concevable d'émuler un minibrowser puis de charger notre petite application Flash dessus. Des exemples assez bien pensés circulent sur Internet ; il reste toutefois à les tester. Le choix de la solution la plus adaptée à nos besoins devra donc être débattu dans le groupe ainsi qu'avec le client, notamment en fonction du temps que nous nous impartirons pour développer la capture, sachant qu'elle est prioritaire dans le projet. Le temps alloué à sa mise en place sera donc assez conséquent. 2.3 Analyse d'image Pour l'analyse d'image, les solutions sont multiples et indépendantes du ou des langages de programmation choisis. En eet, ce domaine est particulièrement prisé par la recherche et le choix dans les algorithmes d'analyse d'images est très conséquent. On peut penser notamment à associer des solutions comme la détection de contour pour repérer le cube sur notre image, grâce par exemple à l'application de matrices comme Sobel ou bien encore Prewitt puis la transformée de Hough pour repérer les lignes sur notre Rubik's Cube. Dans un troisième temps, on pourra essayer de récupérer sur le quadrillage théoriquement obtenu grâce à Hough la couleur de chacune des cases qui devrait être chacune une face d'un cubic. Bien sûr cette théorie sera implacablement démontée par l'épreuve de la réalité ! Mais les solutions ne manquent pas dans ce domaine et peuvent généralement se trouver déjà codées pour eectuer des tests assez rapidement. Il faudra toutefois justement envisager une lourde phase de tests sachant que le public visé est large et que l'utilisateur est considéré par défaut comme imprévisible. Ces tests devront notamment déterminer la tolérance de notre solution en fonction du fond sur lequel se trouve le Rubik's Cube, de son orientation, de la luminosité... an, si celle-ci est trop restrictive, d'envisager soit une toute autre solution, basée par exemple sur le détecteur de Harris ou la segmentation, soit une refonte partielle de celle existante. 21 Chapitre 3 Introduction au projet 3.1 But Le but du projet est donc d'implémenter un logiciel de manipulation, de résolution et d'acquisition de Rubik's Cube. De l'analyse de l'existant, il ressort que l'interface graphique est un des points faibles des logiciels présents sur le marché. La facilité d'utilisation, l'interface de navigation et de manipulation du cube sont donc des points à améliorer. De même, l'acquisition d'un cube pour le manipuler et le résoudre dans le logiciel est une fonctionnalité qui pour le moment n'a pas été souvent implémentée dans un logiciel open source. Quand ce fut le cas (Cube Explorer), le logiciel n'était ni multi-OS ni orienté grand public. 3.2 Justication et priorité Le projet vise donc à améliorer certaines fonctionnalités déjà existantes sur certains logiciels comme par exemple la manipulation du Cube tout en incluant une possibilité rarement présente : l'acquisition. On devra essayer d'avoir une portabibilité sur les systèmes Microsoft (Windows XP), Linux et si possible Mac OS X. Nous implémenterons donc les modules du logiciel dans l'ordre suivant : Modélisation du cube, en orant plusieurs possibilités de navigation : claviers et/ou à la souris. Une représentation du cube en 3D est obligatoire. Tout autre représentation, tel que le cube déplié, est optionnelle. Acquisition de la conguration d'un Rubik's Cube, par un périphérique vidéo, un ensemble d'image ou une saisie des cubics à la souris. Résolution du cube par un algorithme. La mise en place d'un robot manipulant le Rubik's Cube (optionnel voir utopiste). 3.3 Démarche La structure pour le cube n'est pas clairement dénie. Le choix eectué sera déterminant pour le reste du projet car les autres modules du projet notamment l'algorithme permettant la manipulation reposent tous d'une manière ou d'une 22 autre sur cette structure. Bien sûr la mise en place d'un adaptateur est envisageable mais une solution plus élégante sans cette artice serait préférable. De multiples tests seront eectués an de choisir en toute connaissance de cause. Concernant l'acquisition, elle sera faite en utilisant une librairie externe, peutêtre adaptée à nos besoins (cf analyse de l'existant). L'analyse d'image sera faite à partir de la transformée de Hough, voir [DH72] , et/ou du détecteur de Harris, voir [HS88]. Diérentes méthodes pour éclaircir/assombrir l'image selon nos besoins d'analyse des clichés seront également implémentés. Ceci toujours dans le même but à savoir orir une souplesse d'utilisation maximale pour l'utilisateur. Si ces méthodes ne fonctionnent pas, nous utiliserons alors une autre méthode ou compléterons la précédente avec une technique qui consisterait à donner un cadre de placement du Rubik's Cube sur le signal vidéo pour nous faciliter le traitement des résultats. 3.4 Schéma de structure envisagé Fig. 3.1 Schéma de Structure 3.5 Schéma de fonctionnement envisagé Fig. 3.2 Schéma de structure 23 Chapitre 4 Besoins non fonctionnels An de cerner les besoins nécessaires au développement de notre logiciel de modélisation, d'acquisition et de résolution du Rubik's Cube, nous avons commencé par écrire un questionnaire que nous avons soumis à un panel de personnes. L'analyse des réponses à ce questionnaire nous a permis de mieux cerner certains besoins de conception de l'interface, de la partie concernant l'acquisition ou encore la résolution, et enn du système d'aide à mettre en place. Vous trouverez ce questionnaire et son analyse en Annexe (cf. Annexe A). 4.1 Relatifs à l'interface et à la modélisation L'interface de l'application sera conçue de telle manière que l'utilisateur pourra modéliser son cube (en utilisant la souris et la molette de délement de celle-ci), ou encore lancer la phase d'acquisition via les chiers images ou la Webcam, et enn lancer la résolution. Pour ceci, des boutons de raccourcis explicites seront préférés à des menus déroulants trop chargés, dans un souci de clarté. Toujours dans un souci de clarté, il ne faut pas surcharger l'achage ; l'utilisateur pourra donc masquer ou montrer à sa guise l'écran de contrôle de la Webcam. An de tester si l'interface de l'application est intuitive, un test d'utilisation sera eectué sur un panel de testeurs n'ayant pas forcément de connaissances en informatique au sens large, ni en Rubik's Cube. De plus, un questionnaire post-conception reposant sur le même type de questions utilisées dans le questionnaire pré-conception sera réalisé an d'interroger les utilisateurs sur la qualité du logiciel produit et de le comparer à ce qui existe déjà dans le domaine d'application de notre logiciel. Lorsque l'utilisateur fera tourner le cube ou pivoter les diérentes faces, nous devrons garantir un minimum de uidité an d'éviter un achage saccadé. Nous ferons donc des tests sur les nombres d'images par seconde sur plusieurs congurations matérielles diérentes. Si l'on passe sous un seuil xé (par exemple 18 images par seconde), il faudra soit baisser la qualité de l'achage si cela s'avère possible, soit informer l'utilisateur que sa machine n'est pas assez puissante et que par conséquent des ralentissements peuvent se produire. 24 4.2 Relatifs à la manipulation L'utilisateur commandera les diérentes opérations de rotation des faces du cube en se servant de raccourcis claviers ou en entrant une instruction ou une liste d'instructions sur une ligne de commande ceci dans un souci d'orir de la souplesse d'utilisation. Nous utiliserons la terminologie anglo-saxonne : U désigne la face du dessus (Up), D la face du dessous (Down), L la face de gauche (Left), R la face de droite (Right), F la face de devant (Front) et enn B la face de derrière (Back). Ces faces seront dénies par rapport à une référence (soit en modélisant un repère orthonormal, soit par exemple en décidant que la face blanche sera la face supérieure, la rouge la face de gauche, etc.) Ici, l'interprétation correcte des commandes sera testée avec des multitudes d'enchaînements de commandes diérentes an de nous rendre plus conants quant à la qualité de l'interpréteur mis en place. Nous devrons tester des raccourcis claviers utilisés par notre logiciel et d'autres non utilisés, notamment an de vérier qu'il n'y a pas de conits avec des raccourcis claviers propres au système d'exploitation. 4.3 Relatifs à l'acquisition L'acquisition devra être la plus simple possible : ce besoin non fonctionnel nécessitera des compromis entre exibilité et ecacité. En eet, il va falloir xer des règles quant à la manière de fournir six images d'un cube depuis la Webcam ou depuis un disque quelconque (OPTIONNEL) : Si l'utilisateur capture son Rubik's Cube via un périphérique de type Webcam, des conditions d'acquisition particulières devront être respectées an de pouvoir exploiter les images capturées. Par exemple, le cube devra être présenté sur un fond blanc, ou uni, de face,... sont des conditions d'acquisition qui pourront être choisies. Si l'utilisateur fournit six images depuis un moyen de stockage quelconque, il faudra que les chiers respectent un ou plusieurs formats d'image, qu'ils représentent des faces orientées dans un certain sens et qu'ils aient un nom respectant ceux du standard imposé par le logiciel : par exemple, il faudra nommer les chiers : white.jpg ou encore red.bmp suivant le ou les formats d'images choisis. Dans le cas de l'acquisition à l'aide d'un périphérique de type Webcam, si jamais ce périphérique n'était pas détecté, il faudrait le signaler à l'utilisateur et lui proposer de modéliser son Rubik's Cube à l'aide de photos ou à l'aide de la souris pour une modélisation manuelle. Dans le cas où la phase d'acquisition se déroule visiblement bien, il faut garantir que l'image numérisée corresponde bien au cube présenté devant la Webcam ; nous pouvons pour cela demander à l'utilisateur de valider l'image numérisée ou de recommencer la numérisation si l'image a subi une mauvaise acquisition ou un mauvais traitement. Si l'utilisateur décide de fournir six images contenant respectivement chaque face du cube à modéliser, il faut tester les diérents formats de chiers supportés. Si un format n'est pas supporté, il faut le signaler pour que l'utilisateur puisse fournir les images dans un autre format, ou bien lancer une acquisition par Webcam ou une modélisation à l'aide de la souris. 25 Avec ce type d'acquisition, il faudra être très rigoureux quant à l'orientation des faces fournies dans les chiers. 4.4 Relatifs à la validité des données fournies Si l'utilisateur modélise son cube à l'aide la souris, il faut vérier que le cube ainsi donné en paramètre est valide (i.e. possible à résoudre car conforme aux standards de fabrication d'un Rubik's Cube). Ainsi, s'il modélise un cube qui contient par exemple 10 cubics rouges, il faudra le signaler et reprendre la modélisation à la souris. Nous mettrons donc en place une série de tests courts qui compteront par exemple qu'il y a exactement 9 cubics de chaque couleur, que deux couleurs opposées ne se trouvent jamais sur un même cubic ou encore qu'il n'y a pas plusieurs faces centrales de la même couleur, etc. (OPTIONNEL) Si cette batterie de test ne détecte pas d'erreur, nous pourrons lancer en arrière plan l'algorithme de résolution implémenté en le bornant à un nombre de coups très supérieur au nombre maximal de coups nécessaires pour résoudre le Rubik's Cube. Si l'algorithme n'a pas réussi à résoudre le cube après ce nombre de coups, alors nous pouvons en déduire que le jeu de données fourni est erroné, et prévenir l'utilisateur que son cube est invalide. 4.5 Relatifs à la résolution Il faudra lancer l'algorithme de résolution sur un nombre très important de congurations de cubes diérentes an d'être conants quant à la qualité de celui-ci. (OPTIONNEL) Nous implémenterons un compteur de coups restant qui indiquera le nombre de coups séparant l'état actuel du cube de l'état résolu en appliquant l'algorithme implémenté. Il faudra veiller à représenter le cube par la structure la plus adaptée possible, pour pouvoir utiliser l'algorithme de résolution avec cette structure. 4.6 Relatifs à l'implémentation Le programme devra être totalement modulaire : ceci est nécessaire pour avoir un programme propre, améliorable à souhait : si l'utilisateur veut représenter son cube en trois dimensions ou en vue éclatée par exemple, l'utilisation de modules est indispensable ; de même si l'on possède plusieurs algorithmes de résolution, plusieurs moyens de capture ou d'acquisition. Le ou les langage(s) de programmation choisi(s) sera (seront) un choix reposant essentiellement sur l'existence de bibliothèques permettant de gérer les périphériques de type Webcam, d'autres permettant les traitements d'images, la construction et l'achage d'objets en trois dimensions... Notre programme devra être portable : nous souhaitons qu'il fonctionne sous Linux, Windows et Mac OS X. Les deux premiers systèmes d'exploitation seront ceux sur lesquels nous accorderons le plus d'importance dans un premier temps (puisque ce sont ceux sur lesquels nous pourrons faire tous les tests). Nous ferons donc plusieurs exécutions de notre application sur des systèmes d'exploitation 26 diérents en tentant de vérier que son exécution est identique dans le sens où l'application doit répondre à son cahier des charges sur ces diérents systèmes d'exploitation. La portabilité sera un critère majeur du choix des bibliothèques et donc du langage de programmation. Enn, il faudra assurer une compatibilité maximale du logiciel avec les périphérique de type Webcam qui sont très nombreux sur le marché et donc bien sûr pas tous identiques. 4.7 Relatifs à l'installation Destiné à être un logiciel grand public, notre programme de Rubik's Cube se devra d'être facile à installer (notamment dans sa version Windows) pour quiconque connaît un minimum l'informatique. Pour se faire, nous essayerons de nous rapprocher du système d'installation des logiciels commerciaux notamment sous Windows où tout se fait à la souris par l'entremise de quelques clics. On pourra éventuellement faire tester par des utilisateurs notre système d'installation pour voir s'ils trouvent sa mise en place abordable. 27 Chapitre 5 Besoins fonctionnels 5.1 Relatifs à l'interface et à la modélisation Nous devons modéliser un Rubik's Cube en trois dimensions : pour cela, il faudra utiliser les couleurs standards du Rubik's Cube ociel : le rouge, le orange, le vert, le bleu, le jaune et le blanc. De plus, il faudra respecter certaines dispositions des faces centrales : en eet, dans un Rubik's Cube ociel, le blanc est toujours opposé au jaune, le rouge au orange, et enn le bleu au vert. Nous devrons proposer à l'utilisateur une vue en trois dimensions du Rubik's Cube modélisé. (OPTIONNEL : Nous implémenterons aussi une vue éclatée du cube pour l'utilisateur.) 5.2 Relatifs à la manipulation Une fois que le Rubik's Cube sera modélisé et aché à l'écran, l'utilisateur devra pouvoir tourner autour de celui-ci à sa guise. Pour des raisons de facilité et d'ecacité lors de la manipulation du Rubik's Cube, soit il faut modéliser un repère orthonormal, soit il faut remettre le cube dans sa position initiale une fois que l'utilisateur a ni de tourner autour de celui-ci. De cette façon, l'utilisateur pourra toujours se repérer lors de l'exécution d'un mouvement. L'utilisateur utilisera la souris pour faire pivoter le cube comme bon lui semble. Il faudra pouvoir faire pivoter les faces du Rubik's Cube comme l'on ferait pivoter les faces d'un cube réel. Pour ceci, soit l'utilisateur utilisera des raccourcis claviers correspondants aux diérents mouvements possibles, soit il tapera sur une ligne de commande des instructions ou une suite d'instructions reprenant la terminologie anglo-saxonne décrivant tous les mouvements possibles à eectuer. Il faudra donc tester, au moyen de tests en boîte noire, que les faces du cubes pivotent de manière satisfaisante. En eet, un problème pourra se poser si nous ne nous préoccupons pas des cas où l'utilisateur actionnerait plusieurs mouvements à la fois. Il faudra donc vérier au moyen de ces tests que les faces pivotent une par une sur le modèle 3 dimensions. Il faudra aussi coupler ces tests avec des tests en boîte blanche. En eet, il faudra vérier que lors de l'exécution de mouvements simples, puis de plusieurs mouvements simultanés, la structure de donnée représentant le Rubik's Cube soit correctement modiée. 28 5.3 Relatifs à l'acquisition et à l'initialisation L'utilisateur devra pouvoir initialiser un Rubik's Cube dans une position donnée an de le résoudre. Cette initialisation pourra se faire de quatre manières diérentes : Soit en générant un cube aléatoirement à l'aide d'un bouton sur l'interface graphique de l'application. Soit l'utilisateur pourra créer de toute pièce la conguration initiale de son cube en indiquant la couleur de chaque facette du Rubik's Cube. Il pourra donner des couleurs aux cubics grâce à sa souris, en pointant un cubic et en changeant sa couleur grâce à la molette. On considérera dans un premier temps que l'utilisateur fournit un jeu de données qui correspond à une situation réelle, et donc résoluble. (OPTIONNEL : nous implémenterons une vérication du jeu de données fournies) (OPTIONNEL) Soit l'utilisateur fournira six images dans un format que l'on déterminera et qui représenteront les six faces du Rubik's Cube à modéliser. Il faudra pour cela dénir des conventions quant à l'orientation des faces sur les images fournies. Enn l'utilisateur pourra se servir d'une Webcam. Il pourra ainsi présenter son Rubik's Cube devant celle-ci et permettre l'acquisition des six faces en prenant six clichés de son cube. Il faudra bien sûr qu'il ait à sa disposition une petite fenêtre dans laquelle se situera en temps réel la visualisation de ce qu'il s'apprête à numériser. Il faudra présenter à l'utilisateur la face numérisée an qu'il puisse vérier si elle est correcte. Dans le cas contraire, l'utilisateur pourra à nouveau numériser la face incorrecte. 5.4 Relatifs à la résolution Notre client désire avant tout un programme interfacé qui permet la modélisation et l'acquisition d'un Rubik's Cube. Selon l'avancement du développement du logiciel, soit nous intégrerons un algorithme de résolution existant, soit nous en implémenterons un. Quand cet algorithme tournera, le Rubik's Cube modélisé devra eectuer en même temps les mouvements calculés par l'algorithme. L'utilisateur aura la possibilité d'eectuer les mouvements pas à pas an, pas exemple, de pouvoir résoudre son Rubik's Cube physique en même temps. Il existe déjà plusieurs algorithmes concernant la résolution du Rubik's Cube ; dans un premier temps, si la structure de données utilisée le permet, nous tenterons d'intégrer un algorithme déjà écrit dans notre programme. Attention : dans le cas d'un Rubik's Cube généré aléatoirement, l'algorithme de résolution doit réellement résoudre le cube, et non pas se contenter d'empiler les transformations eectuées puis de les refaire en sens inverse ! Nous implémenterons un compteur de coups restants qui indiquera le nombre de mouvements restants tel qu'il est calculé par notre algorithme. Un système d'aide devra être mis en place an que l'utilisateur ait les informations nécessaires à l'utilisation du logiciel. Cette aide concernera notamment : Les diérentes façons de représenter le cube. 29 Les commandes pour faire pivoter le cube. Les commandes pour exécuter les diverses rotations des faces du Rubik's Cube. Les moyens d'acquisition et leur utilisation simpliée. La signication du nombre aché par le compteur (i.e. nombre de coups restants d'après l'algorithme implémenté, et non pas nombre minimal de coups restants, les deux pouvant être diérents) 30 Chapitre 6 Fonctionnalités implémentées et exemples de fonctionnement En ce qui concerne la portabilité, le logiciel n'a pas pu être réellement testé sous Mac OS X, car nous n'avions pas la possibilité d'accéder à un système Mac. 6.1 Interface et visualisation L'utilisateur se voit oertes les possibilités suivantes grâce au logiciel : Fonctionnalités de base Au lancement du logiciel un cube résolu est aché. L'utilisateur peut le manipuler par diérents biais (cf. partie Manipulation). Il peut, s'il le souhaite, générer un cube aléatoirement grâce à l'icône prévue à cet eet. A tout moment du jeu il lui est possible de sauvegarder et de charger son Rubik's Cube. Au lancement de l'application sont proposées les vues en 2 dimensions et en 3 dimensions du Rubik's Cube. Lorsque le panneau d'acquisition vidéo est ouvert, le modèle en 3 dimensions est masqué. Acquisition Concernant l'acquisition, deux options sont disponibles : L'utilisateur lance l'interface de capture vidéo et suit les instructions données par le logiciel quant à l'ordre et l'orientation des faces du Rubik's Cube à présenter devant la caméra. L'utilisateur eectue une capture manuelle. Pour ce faire, il peut utiliser les boutons ou la molette de la souris. La fenêtre d'acquisition manuelle possède également une ligne de commandes pour rentrer directement la séquence de couleurs. On utilise une correspondance lettre ↔ couleur pour décrire ligne par ligne et face par face la conguration du Rubik's Cube. Toutefois lorsque l'utilisateur valide la ligne de commande, celle-ci est entièrement interprétée sans vérier sa validité. 31 Visualisation En ce qui concerne la visualisation, l'interface propose deux modèles : Une visualisation en 3 dimensions qui permet d'acher ou de masquer les axes sortant du centre de chaque face du Rubik's Cube. Ces axes ont pour couleur celle de la face dont ils sont issus. Ils ont pour but de faciliter la manipulation du Rubik's Cube en lui permettant de voir les couleurs des faces cachées. Une visualisation en 2 dimensions, qui représente le cube déplié. 6.2 Manipulation La manipulation du cube peut se faire via deux méthodes : En utilisant les raccourcis claviers. Dans ce cas de manipulation, les fonctionnalités Annuler et Refaire sont à la disposition de l'utilisateur. En utilisant la ligne de commandes. Il est possible d'eectuer au coup par coup les mouvements de la séquence saisie ou de valider la séquence dans son ensemble. Un curseur permet de parcourir la ligne de commandes dans un sens ou dans l'autre. 6.3 Résolution et contrôle de validité L'utilisateur a à sa disposition une fonctionnalité de résolution. Lorsque celui-ci lance le calcul de la solution, la séquence de mouvements correspondante s'ache sur la ligne de commandes. L'utilisateur peut utiliser cette séquence comme indiqué dans la description de la manipulation. L'algorithme de résolution permet également de vérier la validité d'un Rubik's Cube entré par l'utilisateur via les diérentes fonctions d'acquisition. Celuici peut décrire brièvement les raisons de la non-validité du Rubik's Cube. 32 Chapitre 7 Comparaison : Prévisions / Réalisations 7.1 Comparaison : Cahier des charges / Travail réalisé Nous allons détailler ici uniquement les fonctionnalités, obligatoires ou optionnelles, spéciées dans les besoins qui n'ont pas été implémentées ou qui diffèrent de ce qui était prévu. De plus, nous décrirons celles qui ont été ajoutées au cours du développement. 7.1.1 Fonctionnalités obligatoires Toutes les fonctionnalités obligatoires spéciées dans le cahier des charges ont été réalisées, excepté le calcul du nombre d'images par seconde et donc la possibilité de faire des tests sur celui-ci an de baisser la qualité d'achage en cas de valeur trop faible. 7.1.2 Fonctionnalités optionnelles Parmi les fonctionnalités optionnelles, nous avons pu en implémenter certaines ; d'autres, par contre, n'ont pas été réalisées. Fonctionnalités implémentées : Vue dépliée du cube. Vérication des données après acquisition. Cette fonctionnalité est opérationnelle et est basée sur l'algorithme de résolution (cf. Fonctionnalités implémentées). Le compteur de coups restant. Cette fonctionnalité est partiellement implémentée. En eet, le compteur n'est pas actualisé après chaque mouvement. Il indique le nombre de coups restant avant d'atteindre l'état initial du Rubik's Cube uniquement lorsque l'utilisateur déclenche la résolution. Fonctionnalités non-implémentées : Internationalisation de l'interface. Le travail nécessaire à cette fonctionnalité a été en partie réalisé mais n'est pas abouti. L'interface n'est donc 33 disponible qu'en français. Acquisition via des photos des diérentes faces du Rubik's Cube. Cette fonctionnalité n'a pas du tout été abordée. 7.1.3 Fonctionnalités supplémentaires Nous avons implémenté les fonctionnalités suivantes, bien qu'elles ne soient pas inscrites dans le cahier des charges : Généricité de certains modules : structure, visualisation 2D et acquisition. Nous reviendrons plus en détail sur cette généricité dans la description des structures et des algorithmes. Nous avons ajouté un bouton coulissant permettant de régler la vitesse de rotation des faces du modèle en 3 dimensions. Ligne de commande dans le panneau d'acquisition manuelle (cf. Fonctionnalités implémentées). Possibilité de corriger chaque face du cube immédiatement après sa capture vidéo. 34 7.2 Comparaison Planning prévisionnel/Planning eectif Voici le planning initialement prévu, inclus dans le mémoire intermédiaire : Taches \Semaines Implémentation du cube Modélisation 3D Acquisition Webcam Manipulation du cube Interface graphique Traitement Webcam Algorithme de résolution Tests naux Mémoire nal 1 26/02 X X X 2 05/03 X X X X X 3 12/03 4 19/03 5 26/03 6 02/04 X XX X X X XX XX XX XX XX Le planning est un élément déterminant de la gestion d'un projet et le nôtre s'est avéré discutable sur certains points. Par exemple, nous avons consacré beaucoup de temps à la partie structure qui s'est nalement révélée être un élément du logiciel moins fondamental que prévu (cf. Architecture). Nous avons aussi sous-estimé le temps nécessaire à la représentation en 3 dimensions du cube. Si au nal notre cube se révèle plutôt esthétique, son implémentation a subi un retard de presque 10 jours. Le temps perdu sur la structure aurait par exemple pu être consacré à l'amélioration de l'acquisition, notamment la détection des couleurs qui aurait pu être dynamique. De plus, nous aurions pu passer plus de temps sur l'ergonomie de l'interface et sur les tests. 35 Voici donc le planning réellement suivi : Taches \Semaines Implémentation du cube Modélisation 3D Acquisition Webcam Manipulation du cube Interface graphique Traitement Webcam Algorithme de résolution Tests naux Mémoire nal 1 26/02 X X X 2 05/03 X X X 3 12/03 4 19/03 5 26/03 X X 6 02/04 X X XX X X X XX X XX X X XX XX XX 7.3 Comparaison : Architecture prévue / Architecture réalisée L'architecture proposée dans le rapport intermédiaire s'est révélée adéquate pour répondre aux besoins xés par le cahier des charges. Nous avons donc gardé notre architecture modulaire. Chaque module est dédié à un regroupement logique de fonctionnalités. Le changement eectué concerne la classe Rubik qui ne délègue plus le travail à Visualization, alors qu'elle le faisait dans certains cas. Ainsi, les parties interface graphique et visualisation sont séparées de la partie Rubik, an de pouvoir intégrer le Rubik avec n'importe quelle boîte à outils graphique. Cependant, nous n'avons pas totalement réussi à dissocier la visualisation et l'interface que nous utilisons (la classe UnfoldedCube dépend de QT), mais cela peut être modié relativement aisément. La classe MainWindow délègue le travail d'un Rubik's Cube à la classe Rubik et prend en charge les visualisations par l'intermédiaire de classes faisant le lien entre QT et le code OpenGL. De plus, elle gère les interactions utilisateurs ↔ Rubik's Cube. La classe Rubik délègue aux classes Struct, Acquisition et Solver les fonctionnalités qui leurs reviennent. Cette construction nous permet de pouvoir facilement changer la taille du Rubik's Cube. Elle permet en outre de changer les comportements intrinsèques à la classe Rubik en fournissant des sous-classes de celles qui sont déléguées. Par exemple, on peut changer d'algorithme de résolution facilement en créant une classe dérivée de Solver. 36 Chapitre 8 Choix 8.1 Choix du langage Après avoir écrit l'analyse de l'existant et l'architecture, nous avons dû chercher un langage de programmation adapté à nos besoins. L'architecture suggérait un langage objet (organisation structurée, besoin d'héritage...). Cela laissait un choix important de langages car les langages objets sont nombreux (C++, Java, Python, Ruby, Lisp...). Nous avions besoin que notre programme soit portable, ce qui a éliminé de la liste certains langages (Delphi, C#). De plus, nous voulions un minimum de performances, ce qui en a en partie éliminé d'autres (les langages de script, bien qu'il soient probablement susamment performants pour faire fonctionner notre application). Le critère déterminant pour choisir le langage a été la bibliothèque d'acquisition. Nous n'avions pas le temps d'écrire des bindings pour la bibliothèque, surtout que cela pouvait être complexe pour certains langages. La bibliothèque que nous avions choisie (PortVideo) étant écrite en C, il nous fallait un langage capable de pouvoir accéder à du code C directement. A part certains langages marginaux, seul le C++ le permet facilement. Le C++ a donc été le langage choisi car il est celui qui répond le plus à nos critères : objet, performant, portable (à condition de choisir les bonnes bibliothèques), une bibliothèque standard contenant ce dont nous avions besoin, susamment expressif, et capable d'utiliser du code C directement. Cependant, il a tout de même quelques défauts : c'est un langage relativement dur à maîtriser ; le plus gros défaut est la gestion de la mémoire explicite qui peut être laborieuse. 8.2 Choix de la boite à outils graphique Une fois le langage choisi, nous avons eu besoin de choisir quelle GUI nous allions utiliser. Le nombre de boîtes à outils graphique est important, cependant l'objectif de la portabilité a considérablement réduit la liste. Nous avions au choix (en se limitant aux plus connus) : GTK [GTK07a] (C) GTKMM [GTK07b] (version C++ de GTK) QT [Tro07] (C++) 37 WxWidget [WxW07] (C++) Le choix s'est en partie fait sur des expériences personnelles : GTK est beaucoup trop verbeux et il pose certains problèmes sous Windows (fenêtres s'achant mal...). Par conséquent, GTK et GTKMM n'ont pas été choisis. Il restait donc QT et WxWidget. Nous avons préféré opter pour QT car certains membres du groupe connaissaient déjà cet outil. Ce choix a été remis en question au début de la phase de codage : sous Windows, la bibliothèque PortVideoSDL ne compile qu'avec Visual C++ 2003 ; or QT OpenSource Edition ne fonctionne pas avec Visual C++. Nous étions donc confrontés à une impasse, et nous avons pensé que nous allions devoir utiliser WxWidget. En poursuivant les investigations , nous avons trouvé un patch pour QT [Q..07] permettant de le faire fonctionner avec Visual C++. Cela nous oblige à fournir notre version de QT avec l'application (i.e. compilé en statique). Celle-ci faisant une taille de 6 Mo, QT compris, cela ne nous a pas semblé trop gênant. En accord avec notre client et notre chargé de TD, nous avons donc opté pour cette solution. 8.3 Bilbiothèque 3D Au niveau des bibliothèques permettant de rendre des graphismes 3D accélerés, il n'y pas beaucoup de choix : OpenGL1 et Direct3D (partie de DirectX2 ), sans compter les bibliothèques se basant sur ces deux bibliothèques. Si l'on veut en plus avoir un programmme portable, il ne reste que OpenGL, qui bien que moins bien supporté par certains chipsets graphiques intégrés sous Windows, est la seule alternative performante et multi-système. Il existe bien des bibliothèques comme OGRE3D3 qui permettent d'utiliser les deux bilbiothèques, cependant elles vont bien au delà de nos besoins (tout comme OpenGL et DirectX) et surtout elles rajoutent une dépendance non négligeable à notre projet. Nous avons donc decidé d'utiliser OpenGL qui est un standard performant, globalement bien supporté et multi-plateformes. 8.4 Choix des outils Pour nous aider à mener à bien le projet, nous avons été amenés à nous servir d'outils particuliers. Voici comment nous avons choisi ceux-ci : Le gestionnaire de version étant imposé (SVN), nous n'avons pas eu le choix. Cet état de fait ne nous a pas dérangés, car nous voulions de toutes façons utiliser un tel outil. SVN s'est avéré très utile et quasiment indispensable. An de décrire l'avancement du projet, tant pour le client que pour les éventuels visiteurs, nous avons décidé d'utiliser un wiki4 . Celui-ci est disponible sur le site du projet. Nous avons tenté de maintenir au maximum ce site à jour, mais il est vrai que nos temps libres étant extrêmement restreints, nous n'avons 1 http ://www.opengl.org 2 http ://www.microsoft.com/windows/directx/default.mspx 3 http ://ogre3d.org/ 4 PmWiki : www.pmwiki.org 38 pas toujours été très réguliers dans la mise à jour de celui-ci. Pour tester et réparer les fuites mémoire, nous avons choisi d'utiliser Valgrind. En eet, c'est le logiciel Open Source le plus abouti dans ce domaine, les autres alternatives (ccmalloc5 , electric fence6 ) n'étant pas aussi stables ou autant avancées. Nous avons choisi d'utiliser CppUnit7 pour eectuer les tests unitaires. Pour la construction du projet, nous avions le choix entre le standard Unix : Autoconf [Aut07a] et Automake [Aut07b], ou les deux outsiders : CMake [Kit07] ou Scons [Sco07]. Autoconf et Automake ont le défaut de ne pas être portables (sous Windows ils requièrent Cygwin8 ). De plus, malgré le fait que ce soient des standards, ils ne sont pas très pratiques à utiliser, du moins pour des non-initiés. Nous avons notamment trouvé que le fait de devoir écrire plusieurs lignes de code pour un petit programme est très embêtant. Ils requièrent de plus la connaissance de plusieurs outils (script sh, langage m4...) pour pouvoir être utilisés convenablement. Il restait donc CMake et Scons. Le choix s'est fait en fonction d'expériences personnelles, d'avis extérieurs, et de tests sur des programmes courts. CMake a l'avantage d'utiliser un système de générateurs : Sous Windows, il peut produire des Makele pour GNU Make, NMake, et des chiers pour VisualC++... Sous Unix, il peut produire des Makele pour GNU Make et les projets pour KDevelop. Sous Mac OS X, il gère des Makele pour GNU Make et les projets XCode. CMake s'est avéré plus complet, plus rapide et plus adapté à nos besoins que Scons. Nous avons donc choisi d'utiliser CMake. Enn, le dernier outil utilisé est astyle9 dont nous nous servons pour réindenter le code et l'uniformiser. 5 http ://www.inf.ethz.ch/personal/biere/projects/ccmalloc/ 6 http ://perens.com/FreeSoftware/ElectricFence/ 7 http ://cppunit.sourceforge.net/cppunit-wiki/FrontPage ?action=show&redirect=PageD%27Accueil 8 http ://www.cygwin.com/ 9 http ://astyle.sourceforge.net/ 39 Chapitre 9 Description des algorithmes et structures de données 9.1 Structure 9.1.1 Description du module Toute classe implémentant un Rubik's Cube doit hériter de la classe Structure. En eet, cette classe présente l'interface nécessaire et donc les fonctions et mouvements minimums que doit supporter toute classe fournissant une structure de Rubik's Cube. Nous avons choisi d'implémenter une structure de Rubik's Cube dans la classe Cube qui se veut la plus proche possible du modèle physique de l'objet, et ce, dans un souci de facilité de manipulation. Ainsi, un Rubik's Cube est un ensemble de cubics dans un vecteur N*N*N où N est la taille du cube. Un cubic est déni dans la classe Cubic3D, et est un ensemble de 6 faces (Up, Down, Front, Back, Left et Right). Une face, dénie dans la classe Face, possède une couleur (Red, Yellow, White, Blue, Green ou Orange) et un nom. Les couleurs sont dénies dans une classe Colour, qui possède 7 membres statiques de type Colour. Ceci permet d'éviter la création abusive d'objets de type Colour. Avec le recul, nous nous sommes rendus compte que cette construction est trop profonde et présente des classes inutiles. Une classe Cube et une classe Cubic3D auraient certainement su. Cependant, nous étions trop avancés dans le projet pour avoir le temps de changer de stratégie d'implémentation. Les N*N*N cubics composant le Cube sont stockés dans un vector<vector<vector<Cubic3D> > > Ceci permet d'obtenir une structure en 3 dimensions semblable à celle d'un Rubik's Cube réel. Nous aurions pu utiliser un simple vecteur linéaire pour réduire les temps d'accès à chaque Cubic3D. Cependant, la taille des données à traiter étant réduite (la taille d'un Rubik's Cube étant rarement supérieure à 5), cela reste tout 40 à fait acceptable. Pour eectuer les mouvements de face du Rubik's Cube, nous avons implémenté diérentes méthodes : 0 1 2 3 4 5 void void void void void void turnFrontLayer(int layer); turnBackLayer(int layer); turnUpLayer(int layer); turnDownLayer(int layer); turnLeftLayer(int layer); turnRightLayer(int layer); Ces mouvements prennent en paramètre un entier décrivant sur quelle couche du Rubik's Cube s'eectue la rotation. Ils permettent d'eectuer les mouvements standards tels qu'ils sont décrits dans la partie Introduction au domaine . Techniquement, ces méthodes procèdent de la manière suivante : tout d'abord, il faut permuter dans le sens horaire chaque cubic avec son voisin sur la face considérée. Cette opération est eectuée par le biais des méthodes privées suivantes : 0 1 2 3 4 5 void void void void void void moveFront(int size); moveBack(int size); moveUp(int size); moveDown(int size); moveLeft(int size); moveRight(int size); Puisque la structure est générique, il faut eectuer cette opération (la permutation des cubics ) N - 1 fois. De cette façon, un cubic de coin se retrouvera au coin suivant . Une fois cette opération eectuée, il ne reste plus qu'à faire pivoter les cubics de la face considérée sur eux-même dans le même sens que le mouvement en cours. Ceci s'eectue via les méthodes de la classe Cubic3D : 0 1 2 3 4 5 void void void void void void U(); D(); F(); B(); R(); L(); Techniquement, ces méthodes changent juste la couleur des faces du cubic en question. La complexité de cette opération (le mouvement d'une face sur la structure) est donc : ((N −1) permutations de (N ×N −1)) cubics +((N −1) rotations de (N ×N −1)) cubics Soit au nal, une complexité en O(N 3 ). Cette complexité est importante mais elle est à relativiser face à la taille du cube, qui n'excède que rarement 5. Les mouvements standards (up, down, right, back, left et front) sont dénis dans l'interface Structure, directement à partir des fonctions décrites précédemment. Ainsi, up s'eectue grâce à un appel à la fonction : turnUpLayer(1). Les accesseurs en lecture sur la structure sont les suivants : 41 0 1 2 3 4 5 6 7 8 Cubic3D getCubic(int i, int j, int k) const; int *getCube(); int *getFaceUp(); int *getFaceDown(); int *getFaceFront(); int *getFaceBack(); int *getFaceRight(); int *getFaceLeft(); int getSize() const Nous n'allons pas décrire le fonctionnement de toutes ces méthodes, leurs noms susent à exprimer leur rôle et leur fonctionnement. Les méthodes d'accès sur les faces retournent un tableau d'entiers représentant les couleurs des faces visibles des cubics des faces du Rubik's Cube en question. Nous avons xé une correspondance entre int et Colour et donc créé 2 fonctions qui réalisent les conversions : 0 1 Colour int2Colour(int n); int colour2Int(const Colour &c); Les méthodes d'accès aux faces (getFaceX) retournent un tableau d'entiers correspondant aux couleurs des faces, dans un ordre précis : lignes par lignes. La complexité de ces méthodes est en O(N 2 ). La méthode d'accès au cube complet (getCube) retourne un tableau d'entiers correspondant aux valeurs renvoyées par les méthodes d'accès aux faces, dans un ordre précis : up, left, front, right, back , down. Ainsi, le tableau renvoyé aura une taille de N*N*6 (avec 6 le nombre de faces du Rubik's Cube bien évidemment). La complexité de cette opération est de O(6 ∗ N 2 ), c'est à dire O(N 2 ). Les accesseurs en écriture sur la structure sont les suivants : 0 1 2 3 4 5 6 void void void void void void void setCube(int *tab); setFaceUp(int *tab); setFaceDown(int *tab); setFaceFront(int *tab); setFaceBack(int *tab); setFaceRight(int *tab); setFaceLeft(int *tab); Ces fonctions sont les équivalentes des accesseurs en lecture précédemment décrits. Leur complexité est identique. 9.1.2 Points forts, points faibles Les points forts de notre structure sont : La généricité : il est possible d'instancier des Rubik's Cube de taille variables (2*2*2, 3*3*3, etc). Comme précisé plus haut, la similarité de la structure avec le modèle réel, qui permet de manipuler plus facilement la structure. Une interface bien dénie permettant plusieurs implémentations de structure diérentes. 42 Les points faibles sont : Comme précisé plus haut, la profondeur relativement importante de la classe Cube (Cube → Cubic3D → Face → Colour). Nous nous sommes rendus compte en implémentant les autres modules qu'une telle construction de structure était partiellement inutile. En eet, pour interagir avec celle-ci, les autres modules n'utilisent que des tableaux d'entiers, nous aurions donc pu nous contenter d'une structure plus simple. 9.1.3 Problèmes rencontrés Nous n'avons pas rencontré de problèmes majeurs dans cette partie. Cependant, nous avons eu des dicultés à la tester correctement. La description de cette phase sera eectuée dans la prochaine partie ( Tests de validation et de fonctionnement ). 9.2 Acquisition/Traitement 9.2.1 Description du module La classe abstraite Acquisition est la classe responsable de l'acquisition et du traitement. Toute classe implémentant une acquisition (par image) ou une nouvelle méthode de détection doit hériter de cette classe (ou bien sûr d'une de ses dérivées). Elle possède les méthodes suivantes : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public: Acquisition(int size); virtual ~Acquisition(); virtual void close(); virtual unsigned char *getFrame() =0; virtual void getPicture(unsigned char *buffer, int color) =0; virtual int nbFace() { return _face;} virtual void setMirror(bool mir) { _mirror=mir;}; protected: virtual void computeFrame(); void drawSquare(int x, int y); virtual void drawFrame(); map<int, pair<pair<int, int>,Triple<double> > > _centreFaces; Nous allons voir dans un premier temps la partie acquisition. Le constructeur de la classe Acquisition appelle la méthode computeFrame. Cette méthode calcule l'espacement entre les cadres de captures, ainsi que leurs coordonnées. Ainsi, ils ne sont calculés qu'une seule fois. La complexité de cette méthode est en O(N 2 ) (on a N*N cadres). Ces informations sont stockées dans la structure _centreFaces. Celle-ci contient donc les coordonnées des cadres de capture ainsi que la couleur associée. 43 La deuxième méthode utilisée pour l'acquisition est la méthode getFrame qui est virtuelle pure. Les méthodes auxiliaires sont drawSquare qui dessine un cadre ainsi que drawFrame qui dessine tous les cadres de capture. Les complexités de ces méthodes sont respectivement en O(taille d'un cadre de capture) et en O(N 2 ). La classe Webcam implémente l'acquisition par Webcam. Celle-ci s'appuie sur la bibliothèque PortVideoSDL (version 0.3), à laquelle elle délègue l'accès au matériel. Elle implémente la méthode virtuelle getFrame. Celle-ci est chargée de récupérer les images issues du périphérique. Elle y ajoute la superposition des cadres et applique si nécessaire la fonction mirror, fonction appliquant un miroir d'axe horizontal de complexité O(hauteur de l'image/2*largeur de l'image). Sa complexité dépend donc de la complexité inconnue de la récupération du ux sur la Webcam. Elle est donc en O(max(O(hauteur/2*largeur), récupération du ux)). Après l'acquisition, passons à l'analyse des images récupérées. Pour cela nous disposons des méthodes suivantes dans la classe Acquisition : 0 1 2 3 4 virtual virtual virtual virtual virtual int* int* int* void void getPicture(unsigned char *buffer)=0 cubeAcquisition() { getFace() reset() checkFace(unsigned char *buffer,int color) =0; La méthode reset réinitialise l'acquisition. La méthode cubeAcquisition renvoie une copie du cube détecté sous forme de tableau d'entiers (cf. Structure) et getFace renvoie le cube sous forme de pointeur d'entiers (ce n'est pas une copie), elle est donc utilisée pour les corrections sur la capture (cf. Fonctionnalités implémentées). La classe Webcam implémente les fonctions virtuelles getPicture récupérant l'image sur la Webcam et appliquant le ltre bgr2rgb (complexité en O(hauteur * largeur)) ainsi que la fonction mirror si nécessaire ; donc la complexité de getPicture est en O(max(hauteur*largeur, récupération du ux)). La méthode checkFace lance la détection, elle fait appel à la méthode averageColor qui calcule les couleurs moyennes des cadres de capture et les convertit en HSV (espace de couleurs plus adapté à nos besoins, dénissant les couleurs en fonctions de leur teinte, saturation, intensité), de complexité en O(N 2 ∗ tailled0 uncadredecapture2 ) (complexité peu importante vu que N est généralement petit et qu'en pratique nous ne calculons pas la moyenne sur tout le cadre, pour éviter de prendre en compte d'éventuels débordements). Enn checkFace appelle la méthode computeFace qui remplit la face du cube en fonction des valeurs trouvées dans averageColor ; sa complexité est en O(N 2 ). 9.2.2 Points forts, Points faibles Les points forts sont les suivants : Généricité. Permet des acquisitions sur des cubes de taille inférieure à 8 (cf. Extensions possibles). Périphérique Dummy permettant de simuler une fausse Webcam. Résultats d'acquisition satisfaisants. (cf. Tests) Possibilité de correction de la capture par la méthode getFace. 44 Les points faibles sont les suivants : Détection non dynamique, qui peut dépendre de l'éclairage ou de la Webcam (cf. Extensions possibles, Tests). 9.2.3 Problèmes rencontrés Les problèmes rencontrés sont les suivants : Les valeurs seuils des couleurs ont été trouvées de manière empirique ; cela a donc nécessité de nombreux essais et ajustements. 9.3 Modèle 3D 9.3.1 Description du module La visualisation avec GLUT Comme cela a été fait pour les autres fonctionnalités du logiciel, la partie visualisation 3D a été développée de manière indépendante. Ainsi, nous avons dans un premier temps utilisé GLUT1 qui permet de rendre de l'OpenGL dans une fenêtre la plus simple possible sans devoir concevoir une interface quelconque. Il y a ici un avantage indéniable à l'utilisation de GLUT : on est sûr que s'il y a un problème d'achage ou de manipulation, seul le modèle 3D est en cause. Modélisation du cube en 3D Le Rubik's cube est considéré comme un assemblage de 27 cubics. C'est ainsi un tableau 3*3*3 de cubics. Chaque cubic possède ses 3 coordonnées (x, y, z), les rotations qui lui sont associées (RotX, RotY, RotZ) et la liste des rotations à eectuer. Une fois que l'initialisation est faite par la fonction initializeRubik, nous pouvons dessiner le cube. Pour cela, nous avons implémenté la fonction drawRubik qui récupère d'abord les informations propres à chaque cubic an de savoir où le dessiner, et qui les dessine donc un par un. En OpenGL, il n'existe pas de fonction permettant de dessiner directement des objets complexes. Il faut se servir de primitives permettant de dessiner des points, des lignes ou encore des formes géométriques planes particulières. Pour dessiner un cubic, nous commençons donc par dessiner une face colorée. Puis nous appliquons une rotation et/ou une translation et dessinons la face suivante. Cette opération est eectuée 6 fois en prenant soin de changer de couleur à chaque transformation. Ainsi nous avons dessiné notre cubic. Une fois qu'un cubic est dessiné, on eectue une translation du repère an de se placer correctement avant de dessiner le prochain cubic. A la n de l'exécution de drawRubik nous avons donc un Rubik's Cube composé de 27 cubics strictement identiques. La complexité de drawRubik est en O(N 3 ∗ tailledelalistecontenantlesrotationsdescubics). Ce Rubik's Cube n'est à ce stade pas très beau puisqu'il ressemble à un unique cube, les côtés étant composés des faces des cubics de même couleur collées les unes aux autres. C'est pourquoi nous avons créé, avec un logiciel 1 http ://www.opengl.org/resources/libraries/glut/ 45 de dessin basique, une texture représentant le bord noir d'une face d'un cubic. Cette texture est chargée grâce aux fonctions loadGLTextures et imageLoad. Puis lorsque nous dessinons une face d'un cubic, nous lui appliquons la texture. Notre cube ressemble désormais à un Rubik's cube du monde réel. Animation du Rubik's cube Dans un premier temps, la première idée qui nous est venue à l'esprit est de créer des fonctions up(), down(), left(), right(), front(), back() qui prendraient en paramètre un sens an de pouvoir eectuer le mouvement anti-up() par exemple sans faire appel à 3 fois up(). Mais pour éviter de trop grosses duplications de code, nous avons eu une autre idée : nous n'avons écrit qu'une seule procédure rotation qui fera tourner la tranche concernée dans le sens indiqué selon un certain axe (déni en variable globale). Ainsi, faire un up ou un down revient à exécuter une rotation avec 2 valeurs de tranche diérentes. Par soucis de sécurité, nous implémentons la procédure isRotationPossible qui nous indique si une rotation est possible à eectuer au moment de l'appel. En eet si l'on est en train d'eectuer un left, il faut attendre qu'il soit terminé avant d'eectuer un up par exemple. Nous évitons en écrivant ces deux fonctions de grosses duplications de code. Pourtant, en regardant l'implémentation de ces 2 fonctions, on peut constater l'utilisation d'un switch avec dans chaque case des triples boucles imbriquées et des instructions se ressemblant énormément... Mais celles-ci ne sont visiblement pas factorisables. La complexité de ces fonctions est en O(N 3 ). Le point de vue Maintenant que les mouvements sont implémentés, nous devons pouvoir tourner autour de notre Rubik's Cube. Ceci est possible grâce à l'utilisation de variables globales x_scene et y_scene qui permettent lors du drawRubik() de tourner autour du centre de notre repère via l'utilisation de la fonction standard glRotated. Eclairage Nous décidons d'éclairer le Rubik's cube avec 6 lampes, ce qui per- met d'éclairer simultanément tous les côtés du cube sans aucune zone d'ombre. En eet, lorsque nous tournons autour du cube, le repère tourne, entraînant avec lui les lampes ; d'où la nécessité de placer une lampe en face de chaque face. Nous initialisons cet éclairage ainsi que la couleur de fond dans une fonction init appelée dans le main avant glutMainLoop. An d'avoir un rendu agréable, les lumières utilisées sont de type positionelles blanches et nous devons redénir les faces des cubics en leur associant des couleurs de diusion avec glMaterialfv. Amélioration Enn, nous décidons d'acher des axes avec des couleurs cor- respondant aux faces correspondantes (cf. Fonctionnalités implémentées). La complexité de cet achage est en temps constant. 46 Intégration à l'interface Maintenant que le modèle en 3D est implémenté et correspond à nos besoins, nous pouvons l'intégrer à notre interface. De GLUT à QT4 Nous supprimons donc le main et toutes les fonctions se référant à GLUT. Nous réservons un espace de rendu 3D OpenGL dans notre interface en QT4. GLWidget Nous créons donc une classe GLWidget héritant d'un QGLWidget. Notre cher rubik3D.cc est interfacé et propose maintenant des fonctions utilitaires à GLWidget. Ainsi, on écrit les fonctions de mouvements qui appellent toutes doRotate avec les arguments nécessaires, celle-ci appelant à son tour isRotationPossible et rotation. Lien avec la structure Notre cube est maintenant correctement intégré à l'interface du logiciel. Il nous manque cependant une fonctionnalité importante : nous devons pouvoir initialiser le cube dans une position quelconque, et non plus seulement en position résolue ou initiale. Pour ceci, lors de l'exécution de drawRubik, nous fournissons des paramètres à la fonction cube qui est chargée de dessiner un cubic. Ces paramètres sont les couleurs des faces du cubic à dessiner calculées en fonction du contenu de la conguration du Rubik's cube. 9.3.2 Points forts, points faibles Les points forts de ce module sont : Esthétisme général du cube. Possibilité de régler la vitesse de rotation des faces. Les points faibles de ce module sont : Pas de généricité. Pas de gestion du nombre d'images par seconde (cf. Fonctionnalités implémentées, Extensions possibles). Switch de 27 cas dans la fonction cube an de dessiner une conguration particulière du Rubik's Cube (par exemple après une acquisition). Ce switch nuit grandement à la généricité du module. 9.3.3 Problèmes rencontrés Lorsque nous avons voulu implémenter la méthode setCube, nous nous sommes retrouvés confrontés à un problème de conception : il était impossible de changer la couleur des cubics de façon propre ; nous avons envisagé deux solutions : une prenant en compte l'orientation des cubics qui changeait l'implémentation du module Structure ; une autre, celle choisie, qui xe les faces visibles du cube et qui xe une couleur arbitraire aux faces non-visibles. Aucune de ces deux solutions n'est convenable. Durant l'écriture du rapport, nous nous sommes rendu compte qu'il aurait fallu ajouter 6 variables correspondant aux 6 47 faces d'un cubic dans la structure cubic. La méthode setCube aurait alors plus ou moins ressemblé à celle de la classe Cube. 9.4 Interface 9.4.1 Description du module L'interface a été réalisée avec QT4 (cf. Choix). C'est la classe MainWindow qui implémente celle-ci. La plupart des méthodes de cette classe ne sont pas intéressantes, puisqu'il s'agit uniquement de la mise en place des diérents éléments (QPushButton, QLineEdit...). Les fonctionnalités plus délicates à implémenter sont celles correspondants au parsage de la ligne de commande, et le système de mémoire de coups. Le parsage de la ligne de commande est assuré par les méthodes suivantes : 0 1 2 3 4 5 6 7 private slots: QString readCommand(); void prevReadCommand(); bool nextReadCommand(); [...] private: void analyseCommand(QString command); QString reverseCommand(QString command); Les méthodes prevReadCommand et nextReadCommand permettent de déplacer le curseur dans la QLineEdit, et de mettre en sur-brillance la commande en cours d'analyse. Pour la première, celle-ci sera la commande précédant la position du curseur. La fonction readCommand récupère le contenu de la ligne de commande à partir de la position du curseur et calcule la taille de la prochaine commande (celles-ci peuvent être de taille variable). La méthode analyseCommand analyse, comme son nom l'indique, la commande, obtenue par readCommand, qui lui est passée en paramètre. Si celle-ci est valide, elle eectue les mouvements sur les modèles en conséquence. La méthode reverseCommand permet d'inverser la commande qui lui est passée en paramètre. Cela permet notamment de pouvoir gérer les historiques de commandes. Le système de mémorisation de coups est basé sur un tableau de taille xée (nous avons choisis 200), auquel on ajoute chaque mouvement entré au clavier, traduit en commandes. Chaque mouvement incrémente le pointeur du tableau. Lorsque l'utilisateur utilise la fonctionnalité Annuler , le pointeur du tableau est décrémenté, le contenu de celui-ci est inversé grâce à reverseCommand et est analysé. Lorsque l'utilisateur eectue un Refaire le pointeur du tableau est incrémenté et le contenu de celui-ci est ré-inversé puis analysé. L'acquisition vidéo est gérée dans l'interface par un WebcamWidget. Il utilise donc une classe de type Acquisition* pour faire le travail. Au début il avait été conçu de manière multi-threadée : un thread était chargé de récupère les 48 images de la Webcam. Un système de producteur consommateur permettait à QT d'acher les images lorsqu'elles étaient disponibles. Cependant, cette méthode a été abandonnée pour plusieurs raisons : QT ne permet pas qu'un mutex soit déverrouillé par un thread autre que celui qui l'avait verrouillé. Il y avait des problèmes de synchronisations non résolus à la fermeture du programme. QT refusait parfois d'attendre que le thread chargé de récupérer les images nisse, et ce malgré un appel à QThread.wait Après plusieurs essais, les performances gagnées n'étaient pas susantes pour justier le temps passé à corriger les problèmes énoncés. Une autre méthode a donc été utilisée : il n'y a plus de thread créé pour récupérer les images, à la place, un timer (QTimer) déclenche un signal toutes les x millisecondes. Ce signal est connecté au slot update de WebcamWidget, ce qui provoque la récupération d'une image sur la Webcam et l'ache. Un Widget a été ensuite créé : CaptureWidget, an de proposer l'interface graphique à l'acquisition. Il est composé, entre autres, d'un WebcamWidget qui récupère les images de la Webcam et les ache et d'un label donnant les consignes nécessaires à l'utilisateur pour réaliser la capture (face désirée et son orientation). Lorsque l'acquisition d'une face est terminée, il propose une fenêtre permettant de corriger les erreurs d'acquisition. L'acquisition manuelle est gérée dans l'interface par la classe ManualCaptureWidget. Elle repose sur 6 FaceWidget. L'achage d'un FaceWidget est en O(N 2 ). Il existe une subtilité dans cette partie : lors de la construction de ManualCaptureDialog, un paramètre (memSet) permet de dire si le cube doit être initialisé (i.e. si tous les cubics doivent être de couleur grise, sauf les centraux) ou non. Ainsi, si l'utilisateur débute une acquisition, cela lui présente un cube initialisé. Si toutefois l'utilisateur se retrouve en position de devoir corriger une acquisition, cette fonctionnalité permet de redonner à celui-ci la conguration qu'il tentait de valider. La classe UnfoldedCube utilise la classe ManualCaptureWidget où les possibilités d'édition sont désactivées. Elle ajoute les mouvements indispensables à un Rubik's Cube. Ces mouvements sont implémentés avec une complexités en O(N 2 ) 9.4.2 Points forts, points faibles Les points forts de ce module sont : Une interface relativement claire et complète. Un choix varié dans les façons de manipuler un cube. Des possibilités d'acquisition manuelle diverses. Possibilités de corrections de capture (cf. Fonctionnalités implémentées) Les points faibles sont : La classe MainWindow centralise quelques fonctions qui ne devraient pas s'y trouver (parser de la ligne de commande et gestion de l'historique des mouvements). 49 Non générique. 9.4.3 Problèmes rencontrés Après le retrait du thread dans la partie acquisition, alors que nous pensions que cela ne baissait pas les performances (car c'était le cas sous Linux), sous Windows il semblerait que les diérences soient conséquentes. En eet, lors de l'ouverture du panneau de capture, l'application est considérablement ralentie. Ceci dit, nous ne sommes pas sûr de la provenance de ce problème. 9.5 Résolution 9.5.1 Description du module Chaque algorithme de résolution est dans l'obligation d'hériter et donc d'implémenter les méthodes de la classe abstraite Solver. Voici le contenu de cette classe : 0 1 2 virtual string generateSolution(int *cubeConfig) = 0; virtual int checkValidity(int *cubeConfig) = 0; virtual int getNumMoves() = 0; La méthode generateSolution doit retourner une suite de mouvements respectant le standard du parser de commandes de l'interface graphique (décrits dans solver.h). Bien évidemment, cette suite de mouvements doit permettre de résoudre le Rubik's Cube dont la conguration lui a été passée en paramètre. La méthode checkValidity permet un contrôle de la validité du Rubik's Cube passé en paramètre. Cette méthode doit au minimum retourner 0 lorsque le Rubik's Cube est valide. Enn, getNumMoves renvoie le nombre de mouvements de la solution calculée par l'algorithme de résolution. La résolution s'eectue par le biais de la classe LayerByLayer qui utilise la bibliothèque Cubex écrite en C. Cette bibliothèque fournit un algorithme de résolution de Rubik's Cube en couches par couches . Cette bibliothèque est performante, et visiblement sûre (cf. Tests), facilement intégrable à notre programme. Pour intégrer cette bibliothèque, nous avons dû implémenter un adaptateur. En eet, la suite de mouvements retournée par la bibliothèque n'est pas adaptée au standard que nous utilisons. Ainsi, la classe LayerByLayer dérivée de la classe abstraite Solver, réalise cette adaptation. LayerByLayer implémente donc les méthodes décrites ci-dessus. La complexité de l'algorithme utilisé dans la bibliothèque Cubex n'est pas précisée dans les spécications de celle-ci. La densité du code de cette dernière ne nous a pas permis de pouvoir estimer cette complexité. Cependant, les tests eectués sur cette partie, et la rapidité avec laquelle la solution est calculée nous semblent satisfaisants quant à la rapidité de l'algorithme (cf. Tests). 50 9.5.2 Points forts, points faibles Les points forts sont les suivants : La rapidité d'exécution de l'algorithme. La fonction de vérication de validité fournissant de nombreux (7) codes d'erreurs permettant d'interpréter d'où proviens l'invalidité du Rubik's Cube. Les points faibles de ce module sont les suivants : Il est impossible d'être sûr à 100% de la validité de l'algorithme de résolution fourni par la bibliothèque Cubex. La bibliothèque utilisée fournit un des algorithmes les plus basiques qui soient. Nous n'avons pas voulu utiliser l'algorithme de Kociemba, pour plusieurs raisons : tout d'abord il est beaucoup plus coûteux du point de vue processeur/mémoire, il ne fournit pas de contrôle de validité évolué, et de plus, nous n'avions pas le temps d'implémenter un algorithme aussi évolué. 51 Chapitre 10 Tests de validation et de fonctionnements 10.1 Tests aléatoires Pour tester les diérents modules de notre logiciel, au cours et à la n du développement de celui-ci, nous avons souvent utilisé les tests aléatoires. En eet, il est naturel d'éprouver nos diérents modules sur des Rubik's Cube générés aléatoirement, et d'eectuer sur celui-ci les opérations que nous voulons tester. Cette méthode a été utilisée à de nombreuses reprises pour les tests systèmes et les tests en boîte noire que nous décrirons par la suite. Nous avons aussi employé cette méthode pour tester l'algorithme de résolution avant de décider de l'employer dans notre logiciel. Pour contrôler la validité de celui-ci, l'opération consistait à tenter de résoudre des Rubik's Cube générés aléatoirement. 10.2 Tests unitaires Nous avons eectué un certain nombre de tests unitaires pour chaque module du programme. Ainsi, pour les parties structure, résolution et acquisition, nous avons créé au fur et à mesure de la progression de notre travail, des tests spéciques à chaque unité. Nous testons ainsi une par une, parfois à l'aide de tests aléatoires, chacune des fonctionnalités d'un module. A noter que dans cette partie, nous utilisons l'outil CppUnit comme décrit dans la partie Choix . 10.2.1 Module Structure Démarche Pour le module de la structure du Rubik's Cube, nous créons une structure dont nous sommes certain qu'elle corresponde à une conguration particulière du cube, pour ensuite la comparer à la conguration produite par une opération du module. Par exemple, pour tester l'opération left (qui, on le rappelle, consiste 52 à faire tourner dans le sens horaire la face gauche d'un Rubik's Cube), on créé une conguration de Rubik's Cube correspondant à un cube auquel on a eectué ce mouvement : int left_tab[] = {4,2,2,4,2,2,4,2,2,3,3,3,3,3,3,3,3,3,2,1,1,2,1,1,2, 1,1,5,5,5,5,5,5,5,5,5,4,4,6,4,4,6,4,4,6,1,6,6,1,6,6,1,6,6}; Puis, on eectue ce mouvement sur un Rubik's Cube créé par le module, avec l'opération left, et on compare le résultat produit par cette méthode et la conguration prévue : cube =new Cube(3); [...] cube->left(); CPPUNIT_ASSERT_MESSAGE("test 1",!memcmp(left_tab, cube->getCube(), 3*3*6*sizeof(int))); On répète cette opération pour les diérentes méthodes de la structure. On prote de ces tests de méthodes pour utiliser un invariant spécique an de doublement contrôler celles-ci. En eet, lorsque la structure dispose d'un mouvement, elle dispose aussi de son contraire (par exemple : left et antiLeft). On créé donc un Rubik's Cube neutre, c'est à dire un cube dans son état initial ou résolu : cube =new Cube(3); int *neutral=cube->getCube(); Lorsqu'on test la validité d'une opération sur le cube, on eectue directement l'opération inverse sur celui-ci pour vérier que le cube obtenu est bien un Rubik's Cube neutre (i.e. résolu) : cube->right(); CPPUNIT_ASSERT_MESSAGE("test 3",!memcmp(right_tab, cube->getCube(), 3*3*6*sizeof(int))); cube->antiRight(); CPPUNIT_ASSERT_MESSAGE("test 4",!memcmp(neutral, cube->getCube(), 3*3*6*sizeof(int))); On teste ainsi, pour chacun des mouvements sur le Rubik's Cube, qu'appliquer une opération et son contraire ramène bien à un cube en état initial. Pour nir, on teste unitairement chacune des méthodes de cette classe en comparant similairement le résultat obtenu avec une conguration attendue. On peut ainsi tester les méthodes setCube et getCube, setInitialState et isInitialState. 53 Résultats Avant que nous ne commencions à tester les méthodes de ce module à l'aide de CppUnit, mais que nous testions visuellement (avec des achages de résultats sur la console), les résultats nous semblaient corrects alors qu'ils étaient en partie erronés. Nous nous sommes rendus compte de nos erreurs lors des tests d'intégration (cf. Tests d'intégration). Nous avons mis par la suite en place les tests exposés ci-dessus, et grâce à CppUnit, nous limitons les risques liés à une erreur humaine. Ces tests fonctionnent tous et nous permettent d'être sereins quant à la abilité de notre structure. Il est évident que ces tests ne sont pas infaillibles et doivent aussi être couplés, par exemple, aux tests d'intégration. 10.2.2 Module Résolution Démarche Pour le module de résolution du Rubik's Cube (le module solver), nous employons approximativement les mêmes méthodes. Pour tester la validité des opérations disponibles dans ce module, on créé un certain nombre de congurations de Rubik's Cube dont nous sommes certains de la non validité. On créé par exemple, 10 congurations non valides de Rubik's Cube : int cubeInvalid0[]= {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; int cubeInvalid1[]= {0,0,0,0,2,0,0,0,0,0,0,0,0,4,0,0,0,0,0,0,0,0,1, 0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,6,0,0,0,0}; ... Ces congurations sont obtenues par nos soins, à la main ; les risques d'erreurs liées à l'utilisation d'une méthode du programme pour créer ceux-ci sont donc éliminés. Cependant, cela engendre un risque dans la mesure où nous sommes faillibles et inuencés par ce que nous nous attendons à obtenir comme résultat pour créer ces échantillons. Malgré tout, créer un Rubik's Cube invalide est assez aisé ; il sut par exemple de ne donner qu'une seule couleur à tous les cubics. Une fois ces échantillons créés, nous pouvons tester les diérentes méthodes du module. Ainsi, on peut tester la méthode checkValidity (qui retourne un entier correspondant ou non à la validité de la conguration de Rubik's Cube passée en paramètre) : D'une part, on teste un certain nombre de fois avec un cube valide obtenu grâce à la fonction mix construite par échafaudage, d'autre part, on vérie pour chaque conguration non valide créé, que ceux-ci sont bien non valides. CPPUNIT_ASSERT(!solver->checkValidity(cubeValidX->getCube())); CPPUNIT_ASSERT(solver->checkValidity(cubeInvalid1)); [...] On peut eectuer cette opération pour l'ensemble des échantillons créés. 54 On teste de la même façon la méthode generateSolution qui, elle, renvoie soit la solution sous forme de chaîne de caractères, soit renvoie une chaîne vide. De plus, on peut tester la validité de la solution retournée, grâce à la fonction apply_resol créé par échafaudage, qui permet d'analyser et d'appliquer sur une structure la suite de mouvements retournée par l'algorithme de résolution. On eectue cette opération un certain nombre de fois, sur des Rubik's Cube générés aléatoirement par la fonction mix, créé plus haut. Résultats Les résultats dans cette partie sont conformes à ceux attendus. Les méthodes que nous pouvons tester de manière unitaire sont validées par CppUnit. Ces tests restent néanmoins faillibles, puisque nous fournissons des congurations de Rubik's Cube à la main Comme pour le module structure, il est donc évident que nous devons coupler cette méthode de tests avec d'autres techniques. 10.2.3 Module Acquisition Pour le module acquisition, on ne peut tester de manière automatisée que les méthodes getPixel et mirror. En eet, les autres sont des méthodes interactives nécessitant une manipulation de la part d'un utilisateur pour être testées. Cette partie s'eectuera donc dans les tests en boîte noire ou les tests système. Démarche Pour tester les méthodes getPixel et setPixel, on récupère l'image capturée par la caméra dans un buer, puis on récupère un pixel sur ce buer. On applique ensuite ce pixel par la méthode setPixel sur un buer contenant une copie de l'image et on compare les deux buers pour savoir si les pixels sont identiques. Pour tester la méthode mirror, on utilise la méthode de l'invariant : on applique deux fois la méthode mirror sur un buer contenant une image capturée par une caméra. Si au nal le buer avant la double application de cette méthode et après cette application est invariant, alors c'est que la méthode fonctionne. Résultats Comme pour les parties précédentes, les résultats sont conformes à ceux attendus, les comportements des méthodes testées semblent bons. 10.2.4 Module Rubik Démarche Pour tester le module Rubik, nous avons repris les tests appliqués à chaque module et nous les avons fait passés, lorsque cela avait un sens, à la classe Rubik. Les résultats obtenus doivent donc être les mêmes que pour les autres modules. 55 Résultats Comme la classe Rubik ne fait que déléguer le travail aux autres classes, le résultat devrait être positif sous peine d'avoir de gros problèmes. Les tests ont été heureusement validés. 10.3 Tests d'intégration Notre logiciel a été étudié et conçu pour fonctionner de manière modulaire. Ainsi, les parties structure, algorithme de résolution, acquisition, visualisation et interface graphique sont indépendantes et peuvent être remplacées ou enlevées. Partant de cette vision des choses, nous avons conçu notre logiciel de manière complètement individuelle entre les diérentes parties. Ce n'est que dans les dernières phases de développement que nous avons rassemblé le tout. Cette phase de rassemblement est celle qui nous a vus eectuer des tests d'intégration. Ces tests sont en partie liés aux tests en boîte noire : ils font intervenir l'utilisateur qui doit contrôler la cohérence de l'ensemble des éléments une fois regroupés. Ainsi, lorsque nous avons testé les opérations de mouvements sur le Rubik's Cube, nous avons vérié, et ce de manière interactive, que l'ensemble des visualisations se comportaient de façon identique et correcte. Pour chacune des opérations interactives , nous avons agi de même, an de contrôler la cohérence de l'ensemble. Ces tests d'intégration nous ont permis de déceler de nombreux bugs. Par exemple, lorsque nous avons rassemblé la partie structure et la partie visualisation 2D dans l'interface graphique, nous nous sommes aperçus de l'apparition d'incohérences lors des opérations de mouvements ou de sauvegarde/chargement de Rubik's Cube. Nous avions pris soin préalablement de tester chacun des modules indépendamment avec des tests unitaires ; nous ne nous attendions donc pas à trouver des erreurs dans ceux-ci. Au nal, les erreurs se trouvaient dans les deux modules : les opérations de mouvements étaient incorrectes dans la structure, et certains mouvements s'eectuaient dans un mauvais sens sur le modèle 2D. Cet incident nous a fait comprendre toute la nécessite de coupler les diérentes techniques de tests. Si nous n'avions compté que sur les tests unitaires, notre logiciel aurait été complètement buggé . 10.3.1 Substitutivité Il n'y a que pour deux types de modules que nous avons plusieurs implémentations diérentes : la partie visualisation, et la partie acquisition. Dans la partie visualisation, nous avons à notre disposition une représentation en 3 dimensions et une représentation en 2 dimensions du Rubik's Cube. Nous avons donc pu tester la substitutivité de ces 2 composants. Ainsi, nous avons pu essayer notre logiciel avec l'une ou l'autre des visualisations, avant de tester celui-ci avec les 2. Ces tests ne sont pas automatisés, il faut modier la classe Mainwindow, pour que s'ache dans l'interface graphique tel ou tel QWidget. Mais nous avons pu vérier que du moment que les visualisations res- 56 pectaient l'interface prévue à cette eet, l'intégration de celles-ci ne comportait aucun problème. Dans la partie acquisition, nous avons deux acquisitions possibles : l'une faisant intervenir une Webcam, l'autre utilisant la classe Dummy, an de simuler une Webcam lorsque l'utilisateur n'en a pas à sa disposition. La substitutivité ne pose aucun problème ici aussi, du moment que l'interface prévue à cet eet est respectée. Pour les autres modules, nous avons à notre disposition uniquement un modèle de chaque. Il nous est donc dicile de tester la substitutivité de ceux-ci. Cependant, puisque l'intégration de la totalité des modules s'est bien déroulée, il est naturel de penser que cela ne devrait pas poser de problème majeur. 10.4 Tests système 10.4.1 Mouvements d'une face du Rubik's Cube Pour tester le fonctionnement global de cette opération, puisque notre logiciel est un programme complètement interactif et nécessite les actions d'un utilisateur, les tests sur le bon fonctionnement du mouvement d'une face de Rubik's Cube doivent s'eectuer de manière interactive. Par exemple, lorsque le testeur appuie sur la touche U, il doit contrôler que la face Up a bien tourné dans le bon sens, et que le résultat sur le Rubik's Cube est cohérent. 10.4.2 Acquisition d'un Rubik's Cube L'acquisition en elle même, comme décrite dans les tests unitaires, ne peut se tester que de manière interactive. Il faut donc là aussi que le testeur eectue de nombreuses acquisitions an de vérier que celles-ci soient correctement répercutées sur les représentations du Rubik's Cube. Nous avons donc eectué ce travail à de nombreuses reprises an de valider cette opération. On peut également tester les performances de cette fonctionnalité. Quels sont les pourcentages de succès de celle-ci en fonction de la caméra utilisée et des conditions d'éclairages ? Pour eectuer ces tests, il n'y a pas d'autre solution que d'eectuer des mesures et de tracer des courbes de résultats. Nous avons à notre disposition diverses caméras de marques diérentes, et des congurations d'ordinateurs différentes. Les conditions d'éclairages font varier également les taux de réussite de détection des couleurs. Par conséquent, pour chaque Webcam que nous possédons, nous traçons deux graphiques. Un premier représentant le nombre de cubics correctement détectés par couleur dans des conditions d'éclairage naturelles (lumière ambiante), et une seconde dans des conditions d'éclairage articielles. 57 10 8 6 4 2 0 Blanc Bleu Vert Orange Rouge Jaune (a) Nb de cubics (moyenne sur 3 essais) détectés/couleurs (b) Exemple de la qualité d'image produite par la caméra 10.1 Documents illustrant l'opération de capture par la caméra Logitech QuickCam Chat, en conditions d'éclairage naturelles. Fig. On remarque dans la gure précédente que les couleurs bleue, rouge et blanche sont parfaitement bien détectées (100%), mais que le système d'acquisition a beaucoup plus de problèmes pour les couleurs oranges jaune et verte. Lorsque l'on regarde la capture d'écran donnant l'image renvoyée par la Webcam durant l'acquisition, on remarque que même à l'oeil nu, on a du mal à distinguer certaines couleurs. Ainsi, le cubic au milieu à droite n'est pas blanc, mais jaune. Il est évident que si nous sommes incapables de diérencier les deux couleurs, le système le peut encore moins. Le phénomène est identique pour la couleur orange : avec cette caméra le rendu est blanc. La couleur verte est quand à elle plus problématique, nous arrivons à la distinguer à peu près clairement, cependant, elle est tellement proche du bleu, que notre logiciel l'interprète comme tel dans environ 65% des cas pour cette caméra. 10 8 6 4 2 0 Blanc Bleu Vert Orange Rouge Jaune (a) Nb de cubics (moyenne sur 3 essais) détectés/couleurs (b) Exemple de la qualité d'image produite par la caméra 10.2 Documents illustrant l'opération de capture par la caméra Logitech QuickCam Chat, en conditions d'éclairage articielles. Fig. Sur la gure ci-dessus, les tests sont eectués avec le même matériel que précédemment, sauf que cette fois-ci les conditions d'éclairages sont de moins bonne qualité : éclairage articiel. Cependant, les résultats sont sensiblement identiques, à savoir une image de très basse qualité, qui ne permet pas de distinguer les couleurs claires telles que le orange, le jaune et le blanc. Dans ces conditions, le vert est moins bien rendu qu'en pleine lumière naturelle. Par conséquent, le pourcentage d'acquisition baisse considérablement dans cette conguration. Les résultats restent probants pour les cubics rouges, bleus et blancs. 58 Pour la caméra Logitech QuickCam Chat, le résultat n'est donc que très moyennement satisfaisant, car dans des conditions d'éclairage naturelles, la validité de l'acquisition atteint dicilement les 60%, et dans de mauvaises conditions d'éclairage, ce chire baisse à 50%. Cependant, lorsque nous regardons l'image produite par cette Webcam, nous pouvons aisément disculper notre logiciel. Nous avons testé ce module sur d'autres caméras mieux gérées et plus performantes. Voici les résultats des tests eectués sur celles-ci : 10 8 6 4 2 0 Blanc Bleu Vert Orange Rouge Jaune (a) Nb de cubics (moyenne sur 3 essais) détectés/couleurs (b) Exemple de la qualité d'image produite par la caméra 10.3 Documents illustrant l'opération de capture par la caméra Logitech Navigate STX, en conditions d'éclairage naturelles. Fig. Sur la gure précédente, nous voyons les résultats d'acquisitions eectuées avec une autre caméra Logitech. On remarque d'emblée que l'image générée par cette Webcam est de bien meilleure qualité que celle de la précédente. Les conditions d'éclairage sont ici naturelles ; les couleurs sont parfaitement bien distinguables à l'÷il nu. Les résultats d'acquisition sont donc naturellement presque excellents. En eet, ceux-ci sont presque de 100% pour toutes les couleurs, excepté pour le jaune qui possède cependant de très bons résultats. Il est à noter que nous obtenons très souvent des résultats de 100% pour toutes les couleurs avec cette caméra, notamment lors de tests en extérieurs. Ces résultats sont pour le moins excellents et permettent à l'utilisateur une opération d'acquisition presque sans nécessiter de corrections. Cependant, ils sont obtenus dans des bonnes conditions d'éclairage, et pour reéter complètement la puissance de notre acquisition, nous devons aussi tester dans des conditions d'éclairage plus diciles. Voici ces résultats : 59 10 8 6 4 2 0 Blanc Bleu Vert Orange Rouge Jaune (a) Nb de cubics (moyenne sur 3 essais) détectés/couleurs (b) Exemple de la qualité d'image produite par la caméra 10.4 Documents illustrant l'opération de capture par la caméra Logitech Navigate STX, en conditions d'éclairage articielles (lumière jaune). Fig. Ces tests sont eectués en lumière articielle, jaune, donc dans des conditions très diérentes de la lumière naturelle. Les tests restent très bons pour toutes les couleurs sauf pour le blanc sur cette caméra. La très faible luminosité de l'image explique assez bien ce résultat. Certes les chires sont mauvais pour une couleur, mais le pourcentage global est de plus de 83%, ce qui est très bon. Pour cette caméra Logitech, les résultats sont proches de l'excellence, avec un taux de réussite naturellement très proche de 100%. Ce sont donc des résultats très probants, mais qui ne susent pas pour faire de notre acquisition un modèle de abilité, surtout après les tests sur la première Webcam. Voyons désormais les tests pour la troisième caméra : 10 8 6 4 2 0 Blanc Bleu Vert Orange Rouge Jaune (a) Nb de cubics (moyenne sur 3 essais) détectés/couleurs (b) Exemple de la qualité d'image produite par la caméra 10.5 Documents illustrant l'opération de capture par la caméra Creative Instant Webcam, en conditions d'éclairage naturelles. Fig. Sur la gure ci-dessus, nous voyons les résultats d'acquisition eectuée avec la caméra Creative. On remarque que la qualité d'image est moins bonne que pour la précédente caméra, mais que, cependant, les couleurs sont bien distinguées. Les conditions d'éclairage sont ici naturelles. Les résultats sont identiquement excellents avec cette Webcam puisque nous frôlons les 100%. Seul le blanc a quelques petits ratés ; ceci est dû à la clarté relativement faible de l'image. Cependant, les résultats pour cette couleur restent très satisfaisants. Il est à noter que là aussi, nous avons souvent obtenu des résultats de 100% de réussite lors de tests eectués à l'extérieur (i.e. conditions d'éclairage parfaites). 60 10 8 6 4 2 0 Blanc Bleu Vert Orange Rouge Jaune (a) Nb de cubics (moyenne sur 3 essais) détectés/couleurs (b) Exemple de la qualité d'image produite par la caméra 10.6 Documents illustrant l'opération de capture par la caméra Creative Instant Webcam, en conditions d'éclairage articielles (lumière jaune). Fig. Les résultats pour les tests eectués en conditions d'éclairage articielles sont naturellement moins bons que les précédents. Le blanc est encore à 0%, comme pour la seconde caméra Logitech, et l'acquisition perd un peu de précision pour le jaune et le orange. Les résultats sont donc d'un peu moins de 80% de bonnes détections. C'est un peu moins concluant que pour la précédente caméra, mais cela reste quand même un très bon pourcentage, surtout dans des conditions d'éclairage extrêmes . Concluons sur les tests sur la partie acquisition : on a donc pu voir 3 exemples de tests d'acquisition avec 3 caméras diérentes et dans des conditions d'éclairage diérentes. Les résultats sont globalement très positifs, puisque nous frôlons les 100% de bon résultats pour les Webcams qui fonctionnent de manière satisfaisante. L'exemple de la première Webcam est là pour montrer que sur certaines congurations, un utilisateur aura plus de mal à acquérir son cube, puisque l'opération nécessite quand même une certaine qualité d'image. 10.4.3 Résolution d'un Rubik's Cube Il est possible de tester les performances du système en calculant, pour des Rubik's Cube générés aléatoirement, d'une part : le temps moyen mis pour calculer la solution, et d'autre part : le nombre moyen de mouvements calculés par l'algorithme. Pour eectuer ces tests de performances, nous avons conçu un test automatisé par échafaudage : il a fallu créer une méthode mix dans le module solveur spécialement pour générer des Rubik's Cube de manière aléatoire. Cette méthode utilise un nombre pseudo-aléatoire généré par la fonction rand. En fonction de la valeur de ce nombre, on applique tel ou tel mouvement au Rubik's Cube. Ainsi, puisque les mouvements sont testés et (a priori) surs, nous obtenons des congurations de Rubik's Cube aléatoires et valides. Une fois le cube aléatoire généré, on peut lancer la méthode generateSolution de l'algorithme de résolution. Nous avons eectué ce test sur un million de Rubik's Cube générés aléatoirement. Le résultat est le suivant : l'algorithme calcule des séquences de résolution de 99 mouvements en moyennes, en un peu plus de 105 milli secondes en moyenne. Ce résultat est conforme à ce que la bibliothèque annonçait dans sa documentation, et à la théorie de l'algorithme de résolution 61 couche par couche. 10.5 Tests en boîte noire 10.5.1 Questionnaire Lors des tests préparatoires, nous avions eectué un sondage sur un panel d'utilisateurs pour connaître leurs goûts et leurs envies. Nous pouvons désormais soumettre le logiciel à leur approbation. Cette approche nous permet non seulement de tester l'ergonomie du logiciel, mais aussi la stabilité de celuici, puisqu'en multipliant les testeurs, nous multiplions la possibilité de déceler d'éventuels bugs. Le questionnaire que nous avons soumis à notre panel d'utilisateur est disponible en annexe. Cette opération nous a permis de faire remonter des informations intéressantes : Tout d'abord, l'esthétisme de notre logiciel a été plébiscité. Ainsi, presque la moitié des sondés le place en tête en ce qui concerne l'esthétisme, devant l'Esp1 . Cet objectif majeur (un bon esthétisme global) semble donc atteint. Les utilisateurs sont en grande majorité satisfaits des modes de manipulation proposés. Cependant, les moins expérimentés en informatique préfèrent la manipulation des faces à la souris, comme c'est le cas pour l'Esp. Cette fonctionnalité étant trop longue à implémenter, nous n'avons pas eectué de modications. Malgré tout, la majorité des sondés est satisfaite de ce que nous leur proposons. Nous avons eu des réactions liées à l'ergonomie du logiciel. Par exemple, certains utilisateurs se sont plaints de la trop grande sensibilité de la rotation du modèle 3D à l'aide de la souris. Nous avons donc adapté cette opération aux envies des sondés. Certains utilisateurs désiraient de plus pouvoir augmenter la taille de la fenêtre de l'interface, que nous avions xée par commodité. Nous avons donc modié cela ; désormais, un utilisateur peut augmenter la taille de la fenêtre, ce qui permet de grossir le modèle 3D. Nous avons modié une autre fonctionnalité, cette fois-ci concernant l'acquisition manuelle. Au départ, nous ne fournissions comme possibilité pour changer la couleur des cubics que la molette de la souris. Des utilisateurs se sont plaints de ne pas avoir de souris à molette, et donc de ne pas pouvoir eectuer d'acquisition manuelle. Nous avons donc ajouté la possibilité de changer de couleurs avec les boutons gauches et droits de la souris. 1 Logiciel de manipulation de Rubik's Cube en Flash, numéro un en ce qui concerne l'esthétisme lors des tests préparatoires. Disponible ici : http ://www.eviltron.com/modules/esp/ 62 10.5.2 Essais d'homologation Lors des essais eectués avec le client, celui-ci nous a fait part de certaines remarques quand à l'ergonomie du logiciel. Nous nous sommes eorcés de nous plier à ses souhaits, dans la mesure du possible, puisque les essais n'ont pu s'eectuer que vers la n de la phase de codage. Voici la description de ces remarques, et ce que nous avons entrepris pour y remédier : Le client nous a signalé une autre méthode d'acquisition manuelle ergonomique que celle utilisant la souris : la ligne de commande. En eet, il serait possible de donner la liste des couleurs des cubics sur une ligne de commande. Nous avons donc procédé à cet ajout : désormais, on peut non seulement changer la couleur des cubics avec la souris, mais on peut aussi rentrer la liste des couleurs du Rubik's Cube. Cette fonctionnalité n'a pas pu être très optimisée en raison d'un manque de temps, mais elle est parfaitement fonctionnelle. Le client nous a aussi fait part d'un souhait particulier concernant la ligne de commande. En eet, avant que nous ne procédions à des modications, lorsque l'utilisateur lançait l'analyse d'une commande, celle-ci était analysée, répercutée sur les modèles et supprimée de la ligne de commande. Notre client nous a fait part de sa préférence pour un curseur qui surlignerait la commande en cours d'analyse et qui se déplacerait sur la ligne de commande au fur et à mesure des mouvements. Cette fonctionnalité entraînerait l'ajout de la possibilité au curseur de revenir dans le sens inverse, an de pouvoir recommencer la suite de commandes. Nous avons donc implémenté cette fonctionnalité. Nous avons ajouté deux boutons supplémentaires : un qui permet de placer le curseur au début de la ligne de commande, et un qui permet de nettoyer cette dernière. Un autre souhait de notre client était de pouvoir régler la vitesse de rotation du modèle 3D. Nous avons donc ajouté une barre de délement qui permet de régler ce paramètre. Un dernier v÷u du client était de pouvoir stopper l'analyse de la ligne de commande lorsque l'on lance l'exécution complète de celle-ci. Nous avons essayé de procéder à ce changement, mais sans succès. La raison en est que pour exécuter tout le contenu de la ligne de commande, on eectue une boucle sur l'analyse d'une seule commande, jusqu'à ce qu'on arrive au bout de la ligne. L'analyse étant beaucoup plus rapide que les mouvements du modèle 3D, les ordres de mouvements sur le modèle s'enchaînent et celui-ci les empile, ne s'arrête jamais, et empêche toute manipulation pendant qu'il eectue sa série. Le seul remède que nous avons trouvé serait de lancer un thread sur la partie OpenGL, an de pouvoir garder la main sur l'interface. Hélas, le temps qui nous restait à ce moment là était bien trop court pour pouvoir eectuer ce changement. Nous avons tenté de nous plier au maximum aux souhaits du client. Le fait que nous n'ayons pu commencer à rassembler les diérents modules du logiciel que 2 semaines avant le terme du projet, et le fait que nous ayons eu une interface fonctionnelle seulement 1 semaine et demi avant cette date, nous a empêchés de pouvoir soumettre plus amplement notre logiciel aux envies de notre client. 63 10.6 Tests de fonctionnement Notre logiciel étant un programme interactif, le gros travail de tests ne peut se faire de façon automatisée. Nous avons besoin des actions de l'utilisateur pour tester et déceler des éventuels bugs. Le gros du travail de test à donc été une exploration de toutes les fonctionnalités du logiciel, en cherchant dans tous les recoins possibles, la petite bête , et ce, à l'aide de tests en boîte noire . Ce travail a été renforcé par l'apport, comme signalé plus haut, des sondés, qui, en utilisant le logiciel, jouent en quelque sorte le rôle de béta-testeurs. 10.7 Tests en boîte blanche 10.7.1 Tests mémoire Puisque le logiciel est programmé en C++, il est nécessaire de tester la gestion de la mémoire. Pour cela, nous utilisons l'outil Valgrind. D'autres outils ont été testés (ccmalloc entre autres), mais les résultats n'ont pas été probants (ccmalloc faisait planter le programme). La chasse aux fuites mémoire s'est faite en deux temps : dans un premier temps les modules séparés, puis dans un second temps sur le programme complet. Lors de la recherche de fuites mémoire dans les modules, nous avons testé toutes les fonctions une à une, même si le programme en lui-même n'avait aucun sens, le but étant de tester la mémoire. Nous avons lors de cette étape supprimé quelques fuites mémoire. Cette étape a été rapidement terminée. La deuxième étape a été beaucoup plus problématique. En eet, nous avons eu la désagréable surprise de découvrir que QT n'est pas un modèle en ce qui concerne les fuites mémoire. Cela rend la recherche de fuites mémoire très fastidieuse car il faut aller ltrer celles de QT an de trouver les nôtres, ce qui n'est pas une mince aaire malgré la possibilité qu'ore Valgrind de ltrer les résultats. En tournant seulement 5 minutes, la sortie de Valgrind dépasse les 1200 lignes. Nous avons malgré tout repéré des fuites mémoire que nous avons corrigées. Cependant, le doute demeure parfois quant à la provenance de certaines fuites mémoire. Le deuxième problème vient de la mémoire encore atteignable en n de programme. En eet, QT est là aussi problématique. Cependant, il n'est pas le seul fautif. En eet la STL utilise un système de cache pour ne pas avoir besoin de réallouer un objet. La mémoire est vidée uniquement lorsque le cache est plein ; ainsi il arrive que les classes utilisant la STL aient de la mémoire encore atteignable à la n du programme. De la même manière, QT utilise certaines bibliothèques (fontcong notamment) qui ne libèrent pas la mémoire. Cependant, la mémoire encore atteignable à la fermeture du programme est moins problématique que les fuites mémoire. En eet, le système d'exploitation va récupérer la mémoire à la n du programme. 64 Chapitre 11 Extensions et améliorations possibles 11.1 Extensions 11.1.1 Acquisition Nous n'avons pas eu le temps d'implémenter l'acquisition par photos. Cependant, cette extension peut être ajoutée dans l'architecture existante en écrivant une classe héritant de la classe abstraite Acquisition. Une solution possible consisterait à écrire un constructeur prenant en paramètre le chemin d'accès au répertoire contenant les images des faces. Celles-ci porteraient un nom prédéni (up.ext, down.ext... où ext seraient les formats gérés par le programme). Le constructeur lirait les images et les stockerait dans un tableau. La fonction getFrame (servant à acher le ux vidéo) renverrait une image grise, tandis que getPicture réaliserait l'analyse des images (la méthode de détection n'est pas encore déterminée, probablement une méthode utilisant la transformée de Hough). 11.1.2 Résolution Pour la résolution, il y a plusieurs extensions possibles : Rubik's Cube 3*3*3 : Ajouter un algorithme plus performant. On pourrait en eet ajouter l'algorithme de Kociemba. Autre taille de Rubik's Cube : Ajouter des algorithmes pour les autres tailles de Rubik's Cube. En eet, les diérents modules de notre programme sont capables de gérer des cubes de tailles quelconques (y compris l'interface Solver). On doit donc ré-implémenter un algorithme pour chaque taille. 65 11.2 Améliorations 11.2.1 Acquisition L'acquisition par Webcam, bien que fonctionnelle si les conditions d'éclairage sont susantes, pourrait être améliorée en rendant la détection des couleurs dynamique. Cette méthode pourrait permettre de mieux détecter les couleurs dans un environnement sombre. Cela peut se faire en modiant le code de la classe Webcam (ou en en héritant). Lors du lancement de la Webcam, il sut de récupérer les premières images du ux et d'en calculer la moyenne. Suivant le résultat, on décide si l'on est en dessous du palier jour, et on bascule sur le mode nuit qui contient des valeurs diérentes pour la détection des couleurs. Cela peut être ané en proposant plusieurs paliers. Il faudrait également relancer la détection de l'environnement à intervalles réguliers ou à la demande de l'utilisateur an de s'adapter tout seul aux changements d'éclairage. On pourrait également améliorer la gestion des résolutions diérentes. En eet, pour l'instant seul le 320x240 est géré. Cependant, ceci pourrait être amélioré facilement : la classe en elle-même est déjà capable de travailler dans des résolutions diérentes, le seul changement à faire est dans le constructeur de la classe Webcam. Il faut initialiser la classe CameraEngine (sur laquelle s'appuie la classe Webcam) avec la résolution voulue et regarder si cela est autorisé. Si la demande est refusée, il sut de lancer la Webcam en 320x240. Bien que facile, ce changement n'a pas été fait car pour le moment les Webcams gérant nativement des résolutions plus élevées sont inexistantes (ou presque), la plupart font une interpolation, ce qui ne sert à rien dans notre cas (à part peut-être dégrader l'image) ; de plus, au début du projet, cela n'était pas une priorité donc le nécessaire a été fait mais nous ne nous sommes pas focalisés dessus. 11.2.2 Visualisation 3D La visualisation 3D est le seul module (hormis l'interface) à ne pas être générique,puisqu'il ne gère que des Rubik's de taille 3*3*3. Il est donc possible de l'améliorer en le rendant capable de gérer des cubes de taille quelconque. De plus, la visualisation ne possède pas de gestion du nombre d'images par seconde, ce qui est relativement ennuyeux : la vitesse dépend de la puissance du matériel. La présence d'une fonction permettant de gérer la vitesse n'est pas susante. Sur un vieil ordinateur, la vitesse d'achage du Rubik's Cube serait lente même avec le réglage de vitesse le plus élevé. A l'inverse, sur un ordinateur trop puissant, même la vitesse la plus lente serait trop rapide. Cependant, sur tous les ordinateurs testés, cela n'a pas été gênant. 11.2.3 Interface graphique L'interface graphique est fonctionnelle ; cependant elle est orientée vers un Rubik's Cube de taille 3*3*3. Cela n'est pas gênant pour le moment car certains modules ne sont pas totalement génériques, mais à terme, cela sera un handicap. Certaines parties de l'interface graphique sont déjà génériques : la capture manuelle, l'achage d'une face (utilisée par la capture manuelle ainsi que pour la correction de l'acquisition), et l'achage du cube déplié. La ligne de com- 66 mande est également générique. D'autres parties sont pratiquement génériques (sauvegarde et chargement d'un cube). Le plus gros problème vient du fait que l'on ne propose pas à l'utilisateur la taille du Rubik's Cube qu'il veut manipuler. Cependant, les modications nécessaires dans le code ne sont pas importantes (changement dans le code de chargement d'un nouveau Rubik's Cube, chargement à partir d'un chier ou par acquisition, où il sut de créer un nouveau Rubik's Cube de taille voulue). 11.2.4 Divers Certaines parties de code ne sont pas à l'endroit approprié. En eet plusieurs fonctions dans l'interface graphique ne devraient pas y gurer : le parser de la ligne de commande doit soit être dans la classe Structure soit dans une classe parser séparée. La gestion de l'historique des commandes doit être dans une classe séparée, la fonction mix doit être dans la classe Structure, ainsi que les fonctions de chargement et sauvegarde. 67 Bilan Si nous avions du temps supplémentaire Si nous avions eu du temps supplémentaire, nous aurions pu apporter les améliorations et extensions décrites précédemment. Nous aurions pu également porter plus d'attentions aux nitions du logiciel, améliorations des aides, meilleur organisation de l'interface... Nous aurions souhaité pouvoir porter plus d'attention à la portabilité sous MacOSX et Windows. Si nous devions reprendre de zéro Si nous devions reprendre le projet depuis le début, nous aurions porté plus d'attentions à respecter les délais impartis par nous-même. De plus, nous aurions veillé à améliorer non seulement la communication entre les membres du groupe mais aussi l'organisation des tâches. Pour conclure Côtoyer un domaine comme celui du Rubik's Cube était une expérience très agréable, mais il est clair qu'il mérite bien sa réputation de casse-tête parfait. Ce sujet nous a permis de voir diérents aspects techniques de l'informatique : interface, traitement d'images, gestion de matériel, génie logiciel... Nous avons appris durant ce projet que travailler en groupe n'est pas chose aisée. En eet, selon nous, la plus grande diculté pour mener à bien un projet comme celui-ci incombe plus à l'organisation du travail et à la gestion du groupe, plutôt qu'à la partie technique du projet elle-même. Nous sommes satisfaits d'avoir pu mener notre projet à bien en ayant atteint nos objectifs principaux, tout en favorisant l'apprentissage de nouveaux outils ainsi que de nouvelles méthodes de travail. 68 Glossaire Algorithme de Dieu : Il s'agit du meilleur algorithme de résolution de Rubik's Cube en théorie. Il a été calculé grâce à la relation qui existe entre Rubik's Cube et théorie des groupes, que cet algorithme peut résoudre n'importe quel Rubik's Cube en 22 mouvements maximum. Cubic : Un des neuf petits cubes composant une face. Cubiste : Amateur(trice) de la pratique du Rubik's Cube. Framework : Un framework est un ensemble de bibliothèques permettant le développement rapide d'applications. Par exemple, un framework peut fournir toutes les fonctions nécessaires à l'utilisation d'une Webcam dans un programme, sans qu'on ait besoin de s'en préoccuper. Pattern : Un pattern est généralement employé pour désigner un modèle, un motif ou une structure. Ainsi, en informatique, les design patterns sont des modèles de conceptions types. Dans le cas du Rubik's Cube, le mot pattern est employé pour décrire une suite de mouvements permettant d'eectuer une opération pouvant servir à résoudre le cube. SpeedCubing : Discipline consistant à résoudre un Rubik's Cube le plus rapidement possible. SpeedCuber : Personne qui pratique le SpeedCubing. 69 Annexe A Questionnaire Voici le questionnaire utilisé pour le sondage : Quel est votre niveau en informatique : ¤ 1 (aucune connaissance) ¤ 2 (faible) ¤ 3 (moyen) ¤ 4 (bon, conrmé) ¤ 5 (expert) Quel est votre intérêt pour le Rubik's Cube : ¤ 1 aucun ¤ 2 peu d'intérêt ¤ 3 intéressé ¤ 4 cubiste Veuillez classer ces logiciels (de 1 à 4 par ordre de préférence) : Esthétisme général Manipulation Ergonomie Installation Citer une qualité Citer un défaut Note globale JADA's Cube CubeTwister CubeExplorer Esp Quels éléments de notre logiciel vous déplaisent et pourquoi ? Que rajouteriez-vous a notre programme an de le rendre plus intéressant ? 70 Remarques, bugs détectés... : 71 Annexe B Informations insolites sur le Rubik's Cube Découvrons dans cette partie quelques faits divers ou anecdotes concernant le Rubik's Cube. La majorité des informations est issue de l'excellent site [Jar01] qui traite du Rubik's Cube de manière assez originale. B.1 Il existe un timbre hongrois à l'egie du Rubik's Cube. Crédits : [Jar01] Fig. En 1982, d'après des calculs très sérieux, environ un foyer sur trois avait en sa possession au moins un Rubik's Cube dans l'Ouest Américain. Deux nouvelles pathologies médicales se sont répandues chez les plus accrocs au Rubik's Cube. Ces aections sont localisés au pouce et au poignet du cubiste du fait des mouvements fréquents eectués durant les parties. La fréquence de ces mouvements peut aller jusqu'à trois et demi par seconde. Le Cube, grâce à sa renommée internationale et son exportation, a été l'un des facteurs contribuant à la réforme et à la libéralisation de l'économie hongroise entre 1981 et 1985, et qui a nalement mené au passage d'un régime communiste à un régime capitaliste. 72 Un jeune cinéaste canadien s'est inspiré du Rubik's Cube pour écrire son premier long métrage : Cube, lm fantastique. Il reprend le principe du Rubik's Cube. Les six personnages du lm sont enfermés à l'intérieur d'un cube dont chaque face donne sur d'autres cubes, le tout constituant un cube gigantesque. Le problème est que certains cubes sont trués de pièges diaboliques et ultra-sophistiqués, évidemment, des personnages meurent et le lm se concentre ensuite sur un quatuor composé par un architecte taraudé par la culpabilité (il a participé à la construction du cube), une jeune mathéma- Fig. B.2 L'ache du lm Cube ticienne, un autiste et un policier macho et violent. Cube (Canada 1997) de Vincenzo Natali avec Maurice Dean Wint, Nicole DeBoer. Durée : 1h28. Distribué par Métropolitan FilmExport. Andreas Mezey, grand auteur hongrois de sa génération, a écrit une pièce à succès entièrement consacrée au Cube. La pièce a durée trois saisons consécutives dans un grand théâtre de Budapest. Un match de football américain aux Etats Unis a été retardé quand un des joueurs, Bob Blake, n'a pas rejoint le terrain. Il a été trouvé dans le vestiaire en train de s'amuser avec son cube. En 1992, a la conférence d'Edimbourg des chefs d'états européens, John Major, le Premier Ministre britannique de l'époque, a utilisé le Cube pour expliquer à son audience télévisée la complexité du Traité de Maastricht. 73 Annexe C Howto C.1 Chargement Sur le dépôt svn du projet 1 , ou sur le wiki du projet 2 , on peut trouver des binaires pour Windows, Mac OS X, et Linux (32 bits et 64 bits, testés sur Ubuntu). Il sut de décompresser l'archive téléchargée et de lancer l'exécutable. Pour une aide à l'utilisation, consulter la section Utilisation. Si vous décidez de compiler le programme, vous pouvez charger les sources sur le dépôt ou sur le wiki. Les instructions pour la compilation sont données dans la partie suivante. C.2 Compilation Informations sur les dépendances du projet : Sous Windows : Il faut charger QT4 OpenSource Edition [Tro07], ainsi que le patch [Q..07]. Des indications sur l'application du patch et la compilation sont disponibles sur cette page 3 . Il faut compiler QT4 avec les options suivantes : -exceptions -rtti -stl -static -release. Sous Linux : Nous utilisons la boite à outils graphique QT4.De plus, le projet requiert que les bibliothèques suivantes soient installées (version devel) : libraw1394 et libdc1394. Pour l'installation de ces dépendances, nous vous renvoyons au manuel de votre distribution. Sous Mac OS X : Nous utilisons QT4. Nous vous renvoyons au manuel de Mac OS X pour l'installer. Les programmes de tests ne sont compilés que si CppUnit est trouvé. En ce qui concerne la compilation, l'outil qmake (fourni avec QT4) doit être dans le chemin d'accès (variable PATH sur les systèmes UNIX), sinon le chemin 1 https ://services.emi.u-bordeaux1.fr/projet/savane/svn/ ?group=rubikproject 2 http ://projet.rubik.free.fr/pmwiki-2.1.27/pmwiki.php 3 http ://arb.developpez.com/qt4/vc++/compilation/ 74 d'accès à qmake doit être fourni à CMake lors de la conguration. Dans le cas où QT4 et QT3 sont installés (ce qui est fort probable), qmake et moc doivent pointer sur la version 4 (utiliser l'option -v pour connaître la version). Dans le cas où ce n'est pas le cas, il faut eectuer les opérations nécessaires (voir le manuel du système ; sinon ln -s fera l'aaire ; sur les systèmes basés sur Debian, il existe update-alternatives) ou donner le chemin d'accès à qmake (dans sa version 4) dans ccmake. Pour la compilation, nous utilisons donc CMake, qui existe sur Windows, UNIX et Mac OS X. Quelque soit le système, CMake propose l'exécutable cmake qui permet de paramétrer la compilation à partir de la ligne de commande. Cependant, sous Windows, CMake propose CMakeSetup.exe, et sur les autres systèmes, ccmake. Ce sont des interfaces qui permettent une utilisation plus intuitive de CMake. Dans ce tutoriel, nous utiliserons ccmake (ou CMakeSetup sous Windows). Pour compiler le logiciel, il faut de placer dans le répertoire src, créer un répertoire build et aller dans celui-ci, puis lancer ccmake .. (ou CMakeSetup). Les commandes suivantes sont pour les systèmes proposant une console avec sh (ou un de ses dérivés). $cd src; $mkdir build; $cd build $ccmake .. ccmake supporte la complétion avec la touche tabulation. Sous Windows, il vous est demandé quel système utiliser ; il faut répondre Visual Studio 2003. Les indications sont données en bas de la fenêtre. Faites congure ( = tapez 'c' dans ccmake). Si les indications sur les dépendances ont été respectées, vous ne devriez pas avoir de message d'erreur. Si qmake n'est pas trouvé (ou la mauvaise version), il faut donner le chemin d'accès. Dans cette fenêtre, vous pouvez régler le type de compilation avec (CMAKE_BUILD_TYPE), généralement Release ou Debug et le répertoire d'installation (CMAKE_INSTALL_PREFIX). Pour régler plus de détails, vous pouvez passer en mode avancé (tapez 't' dans ccmake). L'option CMAKE_CXX_FLAGS permet notamment de rajouter des options à la compilation, -Wall par exemple. Pour accéder aux fonctions expérimentales du logiciel, rajoutez dans ce champs -DEXPERIMENTAL. Refaites un congure ('c' dans ccmake) puis lancez la génération ('g' dans ccmake). Vous disposez enn dans le répertoire build soit des chiers Visual Studio, soit des Makeles. Nous vous renvoyons au manuel de ces applications. Normalement la compilation devrait bien se passer. En cas de problème, make VERBOSE=1 permet de voir ce qu'il se passe. Il ne reste maintenant plus qu'à installer le programme. Pour cela faites : make install (ou sudo make install, ou su ; make install suivant le répertoire d'installation). C.3 Utilisation Lancez l'application. Sous Linux, le périphérique d'acquisition peut être fourni sur la ligne de commande. Lorsque que cela est proposé, lancez l'acquisi75 tion en 320x240. L'interface ne permet pas de gérer une résolution diérente. Le logiciel respecte un standard : la bleu est la face up, orange la face left, blanche front, rouge right, jaune back et verte down. Vous arrivez sur la fenêtre principale de l'application. Vous avez donc la vue 3D ainsi que la vue dépliée du cube. Une description des icones est disponible dans la barre de statut. Pour manipuler le cube, vous avez deux solutions : Les raccourcis claviers sont les suivants : U : eectue la rotation Up Shift+U : eectue la rotation Up' D :eectue la rotation Down Shift+D : eectue la rotation Down' L : eectue la rotation Left Shift+L : eectue la rotation Left' R : eectue la rotation Right Shift+R : eectue la rotation Right' F : eectue la rotation Front Shift+F : eectue la rotation Front' B : eectue la rotation Back Shift+B : eectue la rotation Back' La ligne de commandes : les lettres U, D, F, B, L, R réalisent les mouvements up, down, front, back, left, right. Le mouvement inverse se fait avec 2 un ' (U', D', F', B', L', R'). Un double moment se fait avec le caractère . Pour les acquisitions, il y a deux types d'acquisition possibles : Manuelle : vous pouvez remplir le Rubik's Cube à l'aide de la souris, les boutons gauche et droit de la souris et/ou la molette permettent de faire déler les couleurs des cubics. La barre en bas de la fenêtre permet de remplir le Rubik en rentrant les couleurs (R=rouge, W=blanc, B=bleu, G=vert, O=orange, Y=jaune). L'ordre des faces est le suivant : up, left, front, right, back, down. Les faces sont entrées ligne par ligne. Vidéo : l'acquisition vidéo permet de créer un rubik à partir d'une Webcam. L'ordre des faces et l'orientation sont indiqués au fur et à mesure. L'utilisateur doit centrer les cubics de la face dans les carrés prévus à cet eet. Un appui sur le bouton capture, ou le raccourci ctr+c, permettent de lancer l'acquisition. Après la capture de la face, une fenêtre permettant de corriger l'acquisition apparaît. Pour la résolution, il sut de cliquer sur l'icone de résolution. La solution apparaît dans la ligne de commandes de l'interface. Il est alors possible de lancer la résolution. 76 Annexe D Valgrind ==8841== Memcheck, a memory error detector. ==8841== Copyright (C) 2002-2006, and GNU GPL'd, by Julian Seward et al. ==8841== Using LibVEX rev 1658, a library for dynamic binary translation. ==8841== Copyright (C) 2004-2006, and GNU GPL'd, by OpenWorks LLP. ==8841== Using valgrind-3.2.1-Debian, a dynamic binary instrumentation framework ==8841== Copyright (C) 2000-2006, and GNU GPL'd, by Julian Seward et al. ==8841== For more details, rerun with: -v ==8841== unable to open 2: No such file or directory ==8841== Invalid read of size 4 ==8841== at 0x4014618: (within /lib/ld-2.5.so) ==8841== by 0x400CFA5: (within /lib/ld-2.5.so) ==8841== by 0x40108ED: (within /lib/ld-2.5.so) ==8841== by 0x4E59031: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x400CFA5: (within /lib/ld-2.5.so) ==8841== by 0x4E591E4: __libc_dlopen_mode (in /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D74049: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D7356A: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D73C63: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D6C52B: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D6B093: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D6ACF9: iconv_open (in /lib/tls/i686/cmov/libc-2.5.so) ==8841== Address 0x5063218 is 48 bytes inside a block of size 49 alloc'd ==8841== at 0x4021620: malloc (vg_replace_malloc.c:149) ==8841== by 0x4D7412F: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D7356A: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D73C63: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D6C52B: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D6B093: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D6ACF9: iconv_open (in /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4A222D3: (within /usr/lib/libQtCore.so.4.2.3) ==8841== by 0x4A22749: (within /usr/lib/libQtCore.so.4.2.3) ==8841== by 0x4A1D558: QTextCodec::fromUnicode(QString const&) const (in /usr/lib/libQtCore.so.4.2.3) 77 ==8841== by 0x498083D: QString::toLocal8Bit() const (in /usr/lib/libQtCore.so.4.2.3) ==8841== by 0x4993EC8: (within /usr/lib/libQtCore.so.4.2.3) ==8841== ==8841== Invalid read of size 4 ==8841== at 0x4014618: (within /lib/ld-2.5.so) ==8841== by 0x40078D9: (within /lib/ld-2.5.so) ==8841== by 0x4010D94: (within /lib/ld-2.5.so) ==8841== by 0x400CFA5: (within /lib/ld-2.5.so) ==8841== by 0x40108ED: (within /lib/ld-2.5.so) ==8841== by 0x4E59031: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x400CFA5: (within /lib/ld-2.5.so) ==8841== by 0x4E591E4: __libc_dlopen_mode (in /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D74049: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D7356A: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D73C63: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D6C52B: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== Address 0x5063218 is 48 bytes inside a block of size 49 alloc'd ==8841== at 0x4021620: malloc (vg_replace_malloc.c:149) ==8841== by 0x4D7412F: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D7356A: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D73C63: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D6C52B: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D6B093: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4D6ACF9: iconv_open (in /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4A222D3: (within /usr/lib/libQtCore.so.4.2.3) ==8841== by 0x4A22749: (within /usr/lib/libQtCore.so.4.2.3) ==8841== by 0x4A1D558: QTextCodec::fromUnicode(QString const&) const (in /usr/lib/libQtCore.so.4.2.3) ==8841== by 0x498083D: QString::toLocal8Bit() const (in /usr/lib/libQtCore.so.4.2.3) ==8841== by 0x4993EC8: (within /usr/lib/libQtCore.so.4.2.3) ==8841== no firewire ports found Texture not loaded Texture not loaded ==8841== ==8841== ERROR SUMMARY: 4214 errors from 102 contexts (suppressed: 171 from 1) ==8841== malloc/free: in use at exit: 380,103 bytes in 3,450 blocks. ==8841== malloc/free: 213,733 allocs, 210,283 frees, 204,314,700 bytes allocated. ==8841== For counts of detected errors, rerun with: -v ==8841== searching for pointers to 3,450 not-freed blocks. ==8841== checked 927,188 bytes. ==8841== ==8841== ==8841== 8 bytes in 1 blocks are definitely lost in loss record 14 of 133 ==8841== at 0x402095F: calloc (vg_replace_malloc.c:279) ==8841== by 0x4867B70: _XCBInitDisplayLock (in /usr/lib/libX11.so.6.2.0) ==8841== by 0x4850CA4: XOpenDisplay (in /usr/lib/libX11.so.6.2.0) ==8841== by 0x419849A: (within /usr/lib/libQtGui.so.4.2.3) 78 ==8841== by 0x4142778: QApplicationPrivate::construct(_XDisplay*, unsigned long, unsigned long) (in /usr/lib/libQtGui.so.4.2.3) ==8841== by 0x4143562: QApplication::QApplication(int&, char**, int) (in /usr/lib/libQtGui.so.4.2.3) ==8841== by 0x806306A: main (in /home/fabien/pdp/memoire/rubikproject/ trunk/rubik/src/build/bin/Rubik) ==8841== ==8841== ==8841== 12 bytes in 1 blocks are definitely lost in loss record 20 of 133 ==8841== at 0x4021DC5: operator new(unsigned) (vg_replace_malloc.c:163) ==8841== by 0x80878ED: init() (in /home/fabien/pdp/memoire/rubikproject/trunk/rubik/src/ build/bin/Rubik) ==8841== by 0x806F991: GLWidget::GLWidget(QWidget*, QGLWidget*) (in /home/fabien/ pdp/memoire/rubikproject/trunk/rubik/src/build/bin/Rubik) ==8841== by 0x8066CFF: MainWindow::createElements() (in /home/fabien/pdp /memoire/rubikproject/trunk/rubik/src/build/bin/Rubik) ==8841== by 0x806C6A0: MainWindow::MainWindow(Rubik*) (in /home/fabien/pdp /memoire/rubikproject/trunk/rubik/src/build/bin/Rubik) ==8841== by 0x806307F: main (in /home/fabien/pdp/memoire/rubikproject/trunk/ rubik/src/build/bin/Rubik) ==8841== ==8841== ==8841== 20 (16 direct, 4 indirect) bytes in 1 blocks are definitely lost in loss record 33 of 133 ==8841== at 0x4021620: malloc (vg_replace_malloc.c:149) ==8841== by 0x4C196D4: driCreateDisplay (in /usr/lib/libGL.so.1.2) ==8841== by 0x4C033CE: __glXInitialize (in /usr/lib/libGL.so.1.2) ==8841== by 0x4BFECA9: glXGetConfig (in /usr/lib/libGL.so.1.2) ==8841== by 0x4BFF1BA: glXChooseVisual (in /usr/lib/libGL.so.1.2) ==8841== by 0x4AF119D: QGLContext::tryVisual(QGLFormat const&, int) (in /usr/lib/libQtOpenGL.so.4.2.3) ==8841== by 0x4AEFFF1: QGLContext::chooseVisual() (in /usr/lib/libQtOpenGL.so.4.2.3) ==8841== by 0x4AF02A3: QGLContext::chooseContext(QGLContext const*) (in /usr/lib/libQtOpenGL.so.4.2.3) ==8841== by 0x4AD42D3: QGLContext::create(QGLContext const*) (in /usr/lib/libQtOpenGL.so.4.2.3) ==8841== by 0x4AEEF3F: QGLWidget::setContext(QGLContext*, QGLContext const*, bool) (in /usr/lib/libQtOpenGL.so.4.2.3) ==8841== by 0x4AD52AB: (within /usr/lib/libQtOpenGL.so.4.2.3) ==8841== by 0x4AF0CF8: (within /usr/lib/libQtOpenGL.so.4.2.3) ==8841== 79 ==8841== ==8841== 20 bytes in 1 blocks are definitely lost in loss record 35 of 133 ==8841== at 0x4021620: malloc (vg_replace_malloc.c:149) ==8841== by 0x4DC195F: strdup (in /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x41983DA: (within /usr/lib/libQtGui.so.4.2.3) ==8841== by 0x4142778: QApplicationPrivate::construct(_XDisplay*, unsigned long, unsigned long) (in /usr/lib/libQtGui.so.4.2.3) ==8841== by 0x4143562: QApplication::QApplication(int&, char**, int) (in /usr/lib/libQtGui.so.4.2.3) ==8841== by 0x806306A: main (in /home/fabien/pdp/memoire/rubikproject/trunk/rubik/src/build/bin/Rubik) ==8841== ==8841== ==8841== 38 bytes in 2 blocks are definitely lost in loss record 52 of 133 ==8841== at 0x4021620: malloc (vg_replace_malloc.c:149) ==8841== by 0x4805837: FcStrCopy (in /usr/lib/libfontconfig.so.1.2.0) ==8841== by 0x4808D6C: (within /usr/lib/libfontconfig.so.1.2.0) ==8841== by 0x4FB43E5: (within /usr/lib/libexpat.so.1.0.0) ==8841== by 0x4FB506C: (within /usr/lib/libexpat.so.1.0.0) ==8841== by 0x4FB606D: (within /usr/lib/libexpat.so.1.0.0) ==8841== by 0x4FB6FB4: (within /usr/lib/libexpat.so.1.0.0) ==8841== by 0x4FAE79A: XML_ParseBuffer (in /usr/lib/libexpat.so.1.0.0) ==8841== by 0x4807E8B: FcConfigParseAndLoad (in /usr/lib/libfontconfig.so.1.2.0) ==8841== by 0x4808185: FcConfigParseAndLoad (in /usr/lib/libfontconfig.so.1.2.0) ==8841== by 0x4808287: (within /usr/lib/libfontconfig.so.1.2.0) ==8841== by 0x48086F1: (within /usr/lib/libfontconfig.so.1.2.0) ==8841== ==8841== ==8841== 156 (36 direct, 120 indirect) bytes in 1 blocks are definitely lost in loss record 76 of 133 ==8841== at 0x4021620: malloc (vg_replace_malloc.c:149) ==8841== by 0x4E34617: (within /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x4E34E65: __nss_database_lookup (in /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x65BCEEB: ??? ==8841== by 0x65BE05C: ??? ==8841== by 0x4DE2E82: getpwuid_r (in /lib/tls/i686/cmov/libc-2.5.so) ==8841== by 0x418E23B: (within /usr/lib/libQtGui.so.4.2.3) ==8841== by 0x418ED65: (within /usr/lib/libQtGui.so.4.2.3) ==8841== by 0x474CB4C: _SmcProcessMessage (in /usr/lib/libSM.so.6.0.0) ==8841== by 0x4760FAB: IceProcessMessages (in /usr/lib/libICE.so.6.3.0) ==8841== by 0x4188897: (within /usr/lib/libQtGui.so.4.2.3) ==8841== by 0x4188916: (within /usr/lib/libQtGui.so.4.2.3) ==8841== ==8841== ==8841== 216 bytes in 1 blocks are definitely lost in loss record 82 of 133 ==8841== at 0x4021620: malloc (vg_replace_malloc.c:149) ==8841== by 0x4889415: _XimOpenIM (in /usr/lib/libX11.so.6.2.0) ==8841== by 0x488925F: _XimRegisterIMInstantiateCallback 80 (in /usr/lib/libX11.so.6.2.0) ==8841== by 0x486E8A7: XRegisterIMInstantiateCallback (in /usr/lib/libX11.so.6.2.0) ==8841== by 0x4568FAD: (within /usr/lib/libQtGui.so.4.2.3) ==8841== by 0x4566FF0: QInputContextFactory::create(QString const&, QObject*) (in /usr/lib/libQtGui.so.4.2.3) ==8841== by 0x413AB61: QApplication::inputContext() const (in /usr/lib/libQtGui.so.4.2.3) ==8841== by 0x417ADB6: QWidget::inputContext() (in /usr/lib/libQtGui.so.4.2.3) ==8841== by 0x41AC106: QWidget::destroy(bool, bool) (in /usr/lib/libQtGui.so.4.2.3) ==8841== by 0x417D6BB: QWidget::~QWidget() (in /usr/lib/libQtGui.so.4.2.3) ==8841== by 0x441AE74: QLineEdit::~QLineEdit() (in /usr/lib/libQtGui.so.4.2.3) ==8841== by 0x4A02040: QObjectPrivate::deleteChildren() (in /usr/lib/libQtCore.so.4.2.3) ==8841== ==8841== ==8841== 316 (256 direct, 60 indirect) bytes in 2 blocks are definitely lost in loss record 86 of 133 ==8841== at 0x4021620: malloc (vg_replace_malloc.c:149) ==8841== by 0x4803313: (within /usr/lib/libfontconfig.so.1.2.0) ==8841== by 0x4803C06: (within /usr/lib/libfontconfig.so.1.2.0) ==8841== by 0x4803D07: (within /usr/lib/libfontconfig.so.1.2.0) ==8841== by 0x4808C50: (within /usr/lib/libfontconfig.so.1.2.0) ==8841== by 0x4FB43E5: (within /usr/lib/libexpat.so.1.0.0) ==8841== by 0x4FB506C: (within /usr/lib/libexpat.so.1.0.0) ==8841== by 0x4FB606D: (within /usr/lib/libexpat.so.1.0.0) ==8841== by 0x4FB6FB4: (within /usr/lib/libexpat.so.1.0.0) ==8841== by 0x4FAE79A: XML_ParseBuffer (in /usr/lib/libexpat.so.1.0.0) ==8841== by 0x4807E8B: FcConfigParseAndLoad (in /usr/lib/libfontconfig.so.1.2.0) ==8841== by 0x4808185: FcConfigParseAndLoad (in /usr/lib/libfontconfig.so.1.2.0) ==8841== ==8841== ==8841== 496 bytes in 2 blocks are possibly lost in loss record 96 of 133 ==8841== at 0x4020820: memalign (vg_replace_malloc.c:332) ==8841== by 0x402087A: posix_memalign (vg_replace_malloc.c:421) ==8841== by 0x4F55693: (within /usr/lib/libglib-2.0.so.0.1200.11) ==8841== by 0x4F560E7: g_slice_alloc (in /usr/lib/libglib-2.0.so.0.1200.11) ==8841== by 0x4F21368: g_array_sized_new (in /usr/lib/libglib-2.0.so.0.1200.11) ==8841== by 0x4F21476: g_array_new (in /usr/lib/libglib-2.0.so.0.1200.11) 81 ==8841== by 0x4F5D4E2: g_static_private_set ( in /usr/lib/libglib-2.0.so.0.1200.11) ==8841== by 0x4F2943B: g_get_filename_charsets (in /usr/lib/libglib-2.0.so.0.1200.11) ==8841== by 0x4F294B0: (within /usr/lib/libglib-2.0.so.0.1200.11) ==8841== by 0x4F5D5B9: g_thread_init_glib (in /usr/lib/libglib-2.0.so.0.1200.11) ==8841== by 0x4F05709: g_thread_init (in /usr/lib/libgthread-2.0.so.0.1200.11) ==8841== by 0x4A17963: QEventDispatcherGlibPrivate::QEventDispatcherGlibPrivate() (in /usr/lib/libQtCore.so.4.2.3) ==8841== ==8841== LEAK SUMMARY: ==8841== definitely lost: 602 bytes in 10 blocks. ==8841== indirectly lost: 184 bytes in 14 blocks. ==8841== possibly lost: 496 bytes in 2 blocks. ==8841== still reachable: 378,821 bytes in 3,424 blocks. ==8841== suppressed: 0 bytes in 0 blocks. ==8841== Reachable blocks (those to which a pointer was found) are not shown. ==8841== To see them, rerun with: --show-reachable=yes 82 Bibliographie [Aut07a] AutoConf. Autoconf - gnu project - free software foundation (fsf), dernière visite le 03/04/07, 2007. http ://www.gnu.org/software/autoconf/. [Aut07b] GNU Automake. Gnu automake, dernière visite le 03/04/07, 2007. http ://sources.redhat.com/automake/. [Bum] Daniel Bump. The mathematics of the rubik's cube. non publié, disponible ici : http ://match.stanford.edu/bump/newcube.pdf, dernière visite : le 05/02/2007. [Can86] J Canny. A computational approach to edge detection. IEEE Trans. Pattern Anal. Mach. Intell., 8(6) :679698, 1986. [Cou97] Philippe Cousin. Resoudre le rubik's cube le plus vite possible, dernière visite le 31/01/2007, 1997. http ://lar5.com/cubefr/indexf.html. [DH72] Richard O. Duda and Peter E. Hart. Use of the hough transformation to detect lines and curves in pictures. Commun. ACM, 15(1) :1115, 1972. [FGJM03] Jean-Pierre Buchweiller Frédéric Gardeux Jacques Marsot. Apports et dicultés de la vision articielle. Jautomatise, (31), novembre 2003. Les auteurs sont de l'Institut National de Recherche et de Sécurité (INRS), cet article est un extrait de la publication faite lors du Safety of Industrial Automated Systems (SIAS) en 2003. [GTK07a] GTK. Gtk+ the gimp toolkit dernière visite le 03/04/07, 2007. http ://www.gtk.org/. [GTK07b] GTKMM. gtkmm - the c++ interface to gtk+, dernière visite le 03/04/07, 2007. http ://www.gtkmm.org. [HS88] C Harris and M.J Stephen. A combined corner and edge detector. In Alvey Vision Conference, pages 147152, 1988. [Jar01] Bruno Jarno. Univers rubik, dernière visite le 07/02/2007, 2001. http ://wrubik.free.fr. [Joy02] David Joyner. Adventures in Group Theory : Rubik's Cube, Merlin's Machine, and Other Mathematical Toys (Broché). 2002. [Joy 7] Professor W.D Joyner. Mathematics of the rubik's cube. non publié, disponible ici : http ://web.usna.navy.mil/ wdj/papers/rubik.pdf, 1996-7. [Kit07] Kitware. Cmake cross platform make, dernière visite le 03/04/07, 2007. www.cmake.org/. 83 [Pic00] Philippe Picart. Les mathématiques et le rubik's cube, dernière visite le 31/01/2007, 2000. http ://trucsmaths.free.fr/rubik.htm. [Q..07] Q../Free. Q... windows edition, dernière visite le 03/04/07, 2007. http ://qtwin.sourceforge.net/qt3-win32/index.php. [Sco07] Scons. Scons : A software construction tool, dernière visite le 03/04/07, 2007. http ://www.scons.org/. [Tro07] Trolltech. Qt trolltech, dernière visite le 03/04/07, 2007. http ://www.trolltech.com/products/qt. [Wik07a] Wikipedia. Les documents multimédia de wikipédia sur le rubik's cube, dernière visite le 31/01/2007, 2007. http ://commons.wikimedia.org/wiki/Category :Rubik%27s_cube ?uselang=fr. [Wik07b] Wikipedia. Speedcubing, dernière visite le 31/01/2007, 2007. http ://fr.wikipedia.org/wiki/Speedcubing. [Wik07c] Wikipédia. Tout sur le rubik's cube, dernière visite le 31/01/2007, 2007. http ://fr.wikipedia.org/wiki/Cube_de_Rubik. [WxW07] WxWidgets. wxwidgets homepage, dernière visite le 03/04/07, 2007. http ://www.wxwidgets.org/. 84