Teaching - 2i013

Enseignants


2i013 - Groupe 5 : Course de voiture

TME 5: Interface graphique

Partie 1: MVC

Définir les interfaces de base de la communication évènementielle

Commençons donc par les cahiers des charges:

 public interface UpdateEventSender {  
        public void add(UpdateEventListener listener);
        public void update();
 }

 public interface UpdateEventListener {
        public void manageUpdate();
 }

Ré-organiser la simulation

Faire en sorte qu'elle devienne émetteur d'évènements:

  • Ajouter la liste des écouteurs en attribut
  • Définir la méthode add
  • Coder la méthode update (cf cours)

Partie vue:

Définir un Observeur:

 public interface ObserverImage {
        public void print(BufferedImage im);
 }

Puis un controlleur qui EST UN écouteur d'évènements et qui gère une liste d'observeurs.

  • Mettre les bons attributs
  • Coder la méthode manageUpdate()
  • Définir un constructeur qui prend en argument un Circuit et qui construit l'image associée. L'image sera stockée en attribut.

Construire enfin l'observeur de Voiture.

Rappel du code de cours:

 public class VoitureObserver implements ObserverImage{
        private Voiture voiture;
        private Color color = Color.yellow;

        public VoitureObserver(Voiture voiture) {
                super();
                this.voiture = voiture;
        }

        public int getX(){ // a inverser pour l'affichage horizontal
                return (int) voiture.getPosition().getX();
        }
        public int getY(){
                return (int) voiture.getPosition().getY();
        }

        public Color getColor() {
            if(voiture.getVitesse()<0.3) // vitesse faible -> cyan
               return new Color(0, (int)(voiture.getVitesse()*255*2), (int) (voiture.getVitesse()*255*2));

            if(voiture.getVitesse() == 0.9)
              return new Color((int)(voiture.getVitesse()*255),  (int) (voiture.getVitesse()*255), 0);

            return new Color((int)(voiture.getVitesse()*255), 0, (int) (voiture.getVitesse()*255));
        }

        public void print(BufferedImage im) {
                im.setRGB(getX(), getY(), getColor().getRGB());
        }
 }

Tests divers

Passer du temps à bien tester cette architecture et à vous assurer qu'elle est fiable.

Vérifier que tous les arguments utiles sont directement accessibles depuis le main.

Partie 2: MVC - SWING

Cahier des charges des nouveaux observeurs

On travaille désormais sur un pointeur graphique (Graphics), c'est à dire

public interface ObserveurSWING {
        public void print(Graphics g);
}

Construction d'une nouvelle vue

Reprendre les transparents de cours pour construire la classe IHMSwing qui étend un JPanel et qui implements l'écouteur d'évènements.

Cahier des charges de l'IHM Swing:

  • extends JPanel
  • implements UpdateEventListener
  • manageUpdate:
        repaint(); // ordre de mise à jour  
        try {      // temporisation (sinon, on ne voit rien)
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
  • manageUpdate (bis): repaint fait un appel à la méthode paint du JPanel qu'il faut donc surcharger.
    public void paint(Graphics g){
        super.paint(g);

        for(ObserveurSWING o:list)
            o.print(g);
    }

Définir de nouveaux observeurs

Exemple de cours sur la voiture:

 public class VoitureObserver implements ObserverSWING{
        private Voiture voiture;

        public VoitureObserveur(Voiture voiture) {
                super();
                this.voiture = voiture;
        }

        public int getX(){
                return (int) voiture.getPosition().getX();
        }
        public int getY(){
                return (int) voiture.getPosition().getY();
        }

        public void print(Graphics g){
                // Attention a l'inversion eventuelle des coordonnees
                g.setColor(color);
                g.drawRect((int) voiture.getPosition().getX(), (int) voiture.getPosition().getY(), 2, 2);
                g.setColor(Color.red);
                g.drawLine((int) voiture.getPosition().getX(),
                                        (int) voiture.getPosition().getY(),
                                        (int) (voiture.getPosition().getX()+voiture.getDirection().getX()*10),
                                        (int) (voiture.getPosition().getY()+voiture.getDirection().getY()*10));

                g.setColor(Color.black);
                g.drawString(String.format("v: %.2f d: (%6.2f, %6.2f) derap: ", voiture.getVitesse(),
                                voiture.getDirection().getX(), voiture.getDirection().getY()) + voiture.getDerapage(),
                                10, 10);


        }
 }

Tester des observateurs de trajectoire, de radar...

Brancher la nouvelle technique d'observation sur le main

NB: vérifier que vous êtes capable de brancher les 2 techniques d'observation en parallèle.

Partie 3: Observeur de voiture avancé

Trouver une image de voiture

Trouver une image, dans un format correct (type PNG), de taille raisonnable (20x30 pixels par exemple)

Nouvel observer de voiture

  • Constructeur: Voiture + nom du fichier (ça doit rester paramétrable par le client)
  • Affichage de la voiture
  // calcul de l'angle à appliquer sur l'image de la voiture pour la rendre
  // cohérente avec la simulation à chaque instant
  // -> dépend de l'image et de l'affichage de l'interface (horizontale ou vertical)
  double angle = Math.PI/2 - Vecteur.angle(voiture.getDirection(), new Vecteur(0, 1));

  // construction d'une transformation
  AffineTransform transform = new AffineTransform();
  // rotation par rapport à un centre à définir (cf javadoc)
  transform.rotate(angle, (car.getWidth() / 2), (car.getHeight() / 2));
  // transformation => transformation d'image
  AffineTransformOp op = new AffineTransformOp(transform, AffineTransformOp.TYPE_BICUBIC);

  // image finale
  BufferedImage carMod = op.filter(car, null);
                g.drawImage(carMod, getX() - 25, getY() - 25, null);

Redimensionner une image... Et faire en sorte d'éviter de la rogner lors des rotations:

1) refaire une version plus petite de l'image originale (mise à l'échelle)

2) la positionner dans une image transparente plus grande (éviter que les bords soient rognés)

this.imCar=ImageIO.read(file); // lecture fichier original
int dim1=78, dim2=100; // nouvelles dimensions
int delta=25; // bords à laisser vierges
BufferedImage resizedImage=new BufferedImage(dim1, dim2, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g=resizedImage.createGraphics();
g.drawImage(imCar, delta, delta, dim1-2*delta, dim2-2*delta, null);
imCar=resizedImage; // image de bonnes dimensions + bords transparents