Préambule

Ce travail vous demande d'effectuer des simples améliorations à une interface utilisateur graphique réalisée en Java. Le code source fourni implémente un logiciel de dessin vectoriel 2D avec une toile zoomable, et comprend 14 classes et environ 1900 lignes de code. Un résumé de chaque classe est fourni, ainsi que des indices pour compléter les modifications demandées.

Avant de commencer, veuillez vous assurer d'avoir un environnement de développement Java sur votre machine. De plus, une partie des directives pour le test sont données par le biais de vidéos QuickTime (fichiers .mov, codec H.264), donc veuillez vous assurer d'avoir une visionneuse capable de jouer de telles vidéos, telle QuickTime player de Apple, ou encore mplayer pour linux.

Code Source

SimpleDraw.zip

Après avoir dézippé le fichier, pour compiler et exécuter avec une invite de commande, faites

   javac *.java
   java SimpleDraw

Pour fonctionner le logicel,

   Bouton gauche de souris + glissement: utiliser l'outil actuel ("Pencil", "Rectangle Select", ou "Move Selection")

   Bouton droit de souris + glissement: afficher un menu contextuel radial

      (Notez bien: la première fois que le menu se fait afficher,
      cela peut prendre un moment avant d'apparaître,
      donc gardez le bouton droit enfoncé et soyez patient)

   Shift + bouton gauche de souris + glissement: défiler la toile

   Shift + bouton droit de souris + glissement: zoomer la toile

Des vidéos montrant les fonctionnalités du logiciel:

SimpleDraw1-fr.mov [10 mégaoctets]

SimpleDraw2-fr.mov [4 mégaoctets]




Code source:
Fichier Classes définies Commentaires
Point2D.java Point2D Classes d'algèbre linéaire et de géométrie. Ces classes implémentent des opérations telles l'addition d'un point avec un vecteur pour obtenir le point résultant (Point2D p1 = ...; Vector2D v1 = ...; Point2D p2 = Point2D.sum(p1,v1);). La classe AlignedRectangle2D implémente un rectangle avec des côtés parallèles aux axes du système de coordonnées, et sert à calculer un rectangle englobant d'un ensemble de points donnés, ou un rectangle englobant d'un ensemble de rectangles donnés (ceci se fait avec les méthodes AlignedRectangle2D.bound()).
Vector2D.java Vector2D
AlignedRectangle2D.java AlignedRectangle2D
Point2DUtil.java Point2DUtil, et deux classes privées qui sont sans intérêt pour les clients Une classe avec des méthodes statiques pour travailler avec les points. Une des méthodes calcule le centroïde (barycentre, ou position moyenne) d'un ensemble de points donné. Une autre méthode sert à tester si un point donné se trouve à l'intérieur d'un polygone donné (cette méthode est utile pour réaliser la sélection en lasso, ainsi que les effets de surbrilliance quand le curseur survol un objet polygonal). Une autre méthode calcule l'enveloppe convexe d'un ensemble de points donnés (cette méthode est utile pour dessiner une "bulle" autour d'un ensemble de points ou d'objets sélectionnés).
GraphicsWrapper.java GraphicsWrapper Cette classe facilite le dessinement en 2D. Les autres classes utilisent GraphicsWrapper pour dessiner, au lieu d'utiliser directement le Java2D (Graphics2D) fourni par l'API de Java. Il y a deux intérêts à utiliser GraphicsWrapper ainsi: d'abord, elle facilite la gestion de zoom et pan dans une vue 2D, simplifiant la conversion entre les systèmes de coordonnées "pixels" et "espace monde" (world space) au moment du dessinement et au moment de la réception d'événements de souris. (Chaque instance de GraphicsWrapper stocke un offset et un facteur de zoom pour effectuer cette conversion.) Deuxièmement, l'implémentation de GraphicsWrapper qui vous est fournie utilise Java2D/Graphics2D pour dessiner, mais on peut faciliment changer cette implémentation pour une autre version de GraphicsWrapper qui utilise OpenGL pour dessiner, ce qui permet de dessiner beaucoup d'objets rapidement, sans avoir à changer le code client. On peut aussi changer pour une implémentation de GraphicsWrapper qui se sert de l'API de Android pour dessiner. (Demandez à l'auteur si vous voulez une copie de l'implémentation OpenGL et/ou Android de GraphicsWrapper.)

En résumé, cette classe encapsule, ou "emballe", les détails de les appels utilisés pour dessiner, d'où le nom "wrapper" (emballeur).

Lorsqu'un client utilise cette classe pour dessiner, à tout moment, le client peut appeler GraphicsWrapper.setCoordinateSystemToPixels() ou GraphicsWrapper.setCoordinateSystemToWorldSpaceUnits(). Suite à un tel appel, toutes les méthodes servant à dessiner des primitives (GraphicsWrapper.drawLine(), GraphicsWrapper.drawRect(), etc.) vont interpréter leurs arguments comme des coordonnées dans le système de coordonnées correspondant.

Les méthodes GraphicsWrapper.convert...() servent à convertir les coordonnées et les points entre les pixels et l'espace monde (world space).

Constant.java Constant Quelques constantes
CustomWidget.java CustomWidget Des classes servant à définir des widgets contextuels. RadialMenuWidget étend la classe de base CustomWidget. Ces classes ne font référence à aucun widget ni aucun événement définis par l'API Java; elles sont indépendentes des widgets SWING et AWT. Cela donne beaucoup de fléxibilité à définir le comportement de ces widgets contextuels.
RadialMenuWidget.java RadialMenuWidget
SimpleDraw.java Stroke, Drawing, MyCanvas, SimpleDraw Les classes principales, et le point d'entrée SimpleDraw.main().

Chaque instance de Stroke stocke une suite de points formant un trait de crayon.

Un Drawing comprend un ensemble de Strokes.

La classe MyCanvas définit une zone rectangulaire où le dessin est affiché, permettant d'interagir avec le dessin. MyCanvas reçoit des événements de souris via ses méthodes mousePressed(), mouseReleased(), mouseMoved(), mouseDragged(), et dessine le contenu de la zone rectangulaire dans sa méthode paintComponent(). Ces mêmes méthodes vont transférer les événements de souris vers une instance de RadialMenuWidget lorsque le menu est affiché, et paintComponent() demandera à l'instance de RadialMenuWidget de se dessiner par dessus la zone rectangulaire lorsque le menu est affiché.

SimpleDraw définit la méthode main(), le point d'entrée du logiciel. SimpleDraw définit aussi la méthode createUI(), qui crée les widgets SWING (barre de menu, boutons radio, etc.) de la fenêtre principale. Lorsque l'utilisateur clique sur un de ces widgets SWING, la méthode SimpleDraw.actionPerformed() est appelée.

Notez bien: lorsque le code reçoit un événement, soit dans MyCanvas.mouse...() ou bien dans SimpleDraw.actionPerformed(), si on veut déclencher un redessinement du contenu de la zone rectangulaire, il est important de ne PAS appeler MyCanvas.paintComponent() directement. Il faut plutôt appeler MyCanvas.repaint(), qui aura pour effet de faire appeler MyCanvas.paintComponent() indirectement un peu plus tard.

Modifications à réaliser

Effectuer 3 (trois) des modifications suivantes au code source.

1. Rajoutez des options pour changer la couleur de l'encre

Rajoutez trois boutons radio dans le panneau d'options dans la partie gauche de la fenêtre principale, servant à choisir entre trois couleurs (noir, rouge, et vert) d'encre. Lorsque l'utilisateur dessine, l'encre créé devra avoir la couleur actuelle. Pour réaliser cela, il va falloir rajouter un membre à la classe Stroke pour stocker sa couleur.

2. Rajoutez une option pour retourner la sélection ("flip")

Rajoutez un bouton dans le panneau d'options dans la partie gauche de la fenêtre principale, servant à retourner les traits sélectionnés autour d'un axe vertical. Autrement dit, lorsqu'on appuie le bouton, les traits sélectionnés devraient subir un "flip" horizontal:

3. Permettre la sélection du trait d'encre le plus près du curseur

Actuellement, lorsqu'on est dans le mode "Rectangle Select", si l'utilisateur clique et relâche sur le même pixel, cela ne sélectionne rien. Modifiez le comportement dans ce mode pour que, quand l'utilisateur clique et relâche sur le même pixel, le trait d'encre le plus près du curseur est sélectionné. Si, par contre, il y a un glissement entre le clique et le relâchement, le comportement actuel de sélection en rectangle devrait rester le même.

4. Permettre une multi-sélection

Lorsque on est en mode "Rectangle Select", permettre à l'utilisateur d'appuyer la touche Ctrl en glissant un rectangle, pour faire rajouter les traits dans le rectangle à la sélection déjà définie. Si l'utilisateur glisse sans appuyer Ctrl, le comportement actuel de remplace l'ancienne sélection devra encore fonctionner.

5. Rajouter un bouton "Frame Selection"

Rajoutez un bouton "Frame Selection", semblable à "Frame Drawing", qui va seulement zoomer et encadrer le rectangle englobant des traits sélectionnés.

Notez bien

Vous êtes libres de programmer les modifications comme vous jugez bon, et vous pouvez modifier toutes les classes. Toutefois, veuillez s.v.p. éviter de changer le formatage, renfoncement ("indentation"), espacement, ou les noms des variables déjà dans le code source. La personne qui évaluera votre code modifié utilisera un outil "diff" pour comparer côte à côte votre code modifié et le code original. Dans une telle comparaison, les modifications superficielles aux espaces ou aux noms des variables sont gênantes.