/*
 * Applet : Rotator
 */

// Bibilothèques utilisées 

import java.util.*;
import java.awt.*;
import java.awt.event.*;
import java.io.*;

/*
 * Classe principale
 */

public class Rotator
    extends Base
       // J'ai écrit la classe Base qui regroupe des fonctionnalités
       // communes à mes applets, voir description.txt
    implements 
	// ActionListener,
	MouseListener,
	MouseMotionListener,
	KeyListener,
	MyMoletteListener,
	LayoutManager
       // signifie qu'il réagit aux actions suivantes 
       // MouseListener       : ... -> clicks souris et entrée/sortie
       //                              du curseur dans la zone d'un Component
       // MouseMotionListener : ... -> mouvements de la souris dans la
       //                              zone d'un component
       // KeyListener      : clavier
       // MyMoletteListener : pour être notifiés de l'action de
       //  l'utilisateur sur un gadget que j'ai écrit qui s'appelle MyMolette
{
    /*
     *                             Champs
     */

    /*
     * Je mets ici les constantes utilisées dans le placement des 
     * zones de l'applet
     */
    public static int
	XOFFSET=50, // position de l'aire de jeu (Upper Left corner)
	YOFFSET=50, // idem
	BOX_SIZE=35, // Taille des boîtes contenant les coordonnées
	BOX_SEP=10, // Separation entre les boîtes
	BUT_SIZE=14, // Taille des boutons + et - (pair)
	BUT_OFF=5; // Distance des boutons à la case

    //    public static double smallAngle=0.01;
    public static int DIV=628;

    public Image dePic,circlePic;

    public Rectangle bounds; // mémorise la taille de la zone d'affichage

    public int mouseX,mouseY; // mémorise les coords du curseur :
                              // utilisé dans keyListener

    public int clickedX,clickedY;
    public double savedoffx,savedoffy;

    // Coordonnée de la matrice
    public double[][] matrice;

    //    public double[][][] rotation;
    public double[] SIN,COS;
    public double[] SINbis,COSbis;
    public double[][][] rotMatr;

    //
    public double[] axis; // Stocke le résultat du calcul de l'axe de rotation
    public double[][] auxMatr; // utilisée dans ledit calcul

    public double[][] memMatr; // matrice mémorisée lors des rotations

    // 
    public Font zeFont;

    //
    public Color COL1,COL2;

    /*
     * Géométrie des boutons : on a 2*n boutons + et 2*n boutons -
     * numérotation : k=a*3+p avec 
     * a dans {0,1}, p dans {0,1,2}
     * a : pré ou post, p : axe.
     */
    public MyMolette molette[]; // Ces molettes règlent la matrice
    public Rectangle butAlea; // Ce bouton tire une matrice aléatoire
    public Rectangle butTurn; // Pour changer le mode d'affichage

    //    public MyMolette whichMolette;

    public static int btSIZE=20;
    public static int nModes=4; // Nombre de modes d'affichage
                                // (=nb de positions du bouton)
    public int affMode;
    public int[] xV1,yV1,xV2,yV2;
    public static int R1=btSIZE/2-2;
    public static int R2=btSIZE/2+3;

    public static int CIRCDIV=40;

    /*
     *                          Sous-classes
     */

    // Aucune !

    /*
     *                           Méthodes
     */

    /*
     * Mémorise la taille de la zone d'affichage et calcule 
     * la position des objets dans icelle
     */
    public void storeBounds() {
	bounds=getBounds(); // prendre la taille de la zone
                            // d'affichage de la fenêtre

	/*
	int w=(bounds.width-x2-2*BUTTON_SIZE)/3;
	but1=new Rectangle(x2+w,y2-BUTTON_SIZE,BUTTON_SIZE,BUTTON_SIZE);
	but2=new Rectangle(x2+2*w+BUTTON_SIZE,y2-BUTTON_SIZE,BUTTON_SIZE,BUTTON_SIZE);
	but3=new Rectangle(x2+w,y2-BUTTON_SIZE-30-SM_BUT_SIZE,SM_BUT_SIZE,SM_BUT_SIZE);
	*/
    }

    /*
     * Le modulo des mathématiciens
     */
    public int myMod(int a, int m) {
	return(a >= 0 ? a % m : m-1-((-a-1) % m) );
    }

    /*
     * Tire une matrice de rotation aléatoire
     */
    public void randomMatrix() {
	// Meilleure méthode : spécifique à la dimension 3
	// utilise l'agréable propriété que la répartition
	// en proba de 'z' est uniforme sur [-1,1]
	double theta;
	double x,y,z,d;
	// tirer z uniformément entre -1 et 1, theta entre 0 et 2*Pi
	z=Math.random()*2.0-1.0;
	theta=Math.random()*2.0*Math.PI;
	d=Math.sqrt(1.0-z*z);
	x=d*Math.cos(theta);
	y=d*Math.sin(theta);

	// Déterminer quelqu'un d'orthogonal à lui :
	double u,v,w;
	if(3*z*z<1) {
	    d=Math.sqrt(1.0-z*z);
	    u=-y/d;
	    v=x/d;
	    w=0;
	}
	else if(3*y*y<1) {
	    d=Math.sqrt(1.0-y*y);
	    u=z/d;
	    v=0;
	    w=-x/d;
	}
	else {
	    d=Math.sqrt(1.0-x*x);
	    u=0;
	    v=-z/d;
	    w=y/d;
	}

	// Le troisième larron : par produit vectoriel
	double a,b,c;
	a=y*w-z*v;
	b=z*u-x*w;
	c=x*v-y*u;

	double phi,cs,si;
	phi=Math.random()*2.0*Math.PI;
	cs=Math.cos(phi);
	si=Math.sin(phi);

	// L'angle alpha de rotation a pour répartition
	// (1/pi).(sin alpha)^2.(d alpha) : PEU IMPORTE

	matrice[0][0]=x;
	matrice[1][0]=y;
	matrice[2][0]=z;
	matrice[0][1]=u*cs+a*si;
	matrice[1][1]=v*cs+b*si;
	matrice[2][1]=w*cs+c*si;
	matrice[0][2]=matrice[1][0]*matrice[2][1]-matrice[2][0]*matrice[1][1];
	matrice[1][2]=matrice[2][0]*matrice[0][1]-matrice[0][0]*matrice[2][1];
	matrice[2][2]=matrice[0][0]*matrice[1][1]-matrice[1][0]*matrice[0][1];
    }

    public void switchTurn() {
	affMode=(affMode+1) % nModes;
    }

    public void computeAxis() {
	for(int i=0; i<3; i++) {
	    for(int j=0; j<3; j++) {
		auxMatr[i][j]=(matrice[i][j]+matrice[j][i])/2;
	    }
	}
	double c=(matrice[0][0]+matrice[1][1]+matrice[2][2]-1)/2.0;
	for(int i=0; i<3; i++) {
	    auxMatr[i][i]=auxMatr[i][i]-c;
	}
	int maxIndex;
	double max,maxeur;
	max=0.01;
	maxIndex=-1;
	for(int j=0; j<3; j++) {
	    maxeur=Math.abs(auxMatr[0][j])+Math.abs(auxMatr[1][j])+
		Math.abs(auxMatr[2][j]);
	    if(maxeur>max) {
		maxIndex=j;
		max=maxeur;
	    }
	}
	if(maxIndex>-1) {
	    double d=0;
	    for(int i=0; i<3; i++) {
		d+=auxMatr[i][maxIndex]*auxMatr[i][maxIndex];
	    }
	    d=Math.sqrt(d);
	    for(int i=0; i<3; i++) {
		axis[i]=auxMatr[i][maxIndex]/d;
	    }
	}
	else {
	    for(int i=0; i<3; i++) {
		axis[i]=0.0;
	    }
	}
    }

    /*
    public void myDrawArc(Graphics g,
	  int cx, int cy, int r, int a0, int al) {
	g.drawArc(cx-r,cy-r,2*r,2*r,a0,al);
    }
     */

    /*
     * Constructeur de la classe principale
     */
    public void init() {

	// Toujours appeler le constructeur de la classe mère

	super.init();

	// Message d'aide

        StringBuffer auxString=new StringBuffer(
  "Mode d'emploi de l'applet\n"
+ "\n" 
+ "Affichage :\n"
+ "\n"
+ "  -  Applet manipulateur de matrices de rotations dans R^3. \n"
+ "\n"
);
	auxString.append(
  "\n"
+ "Commandes :\n"
+ "\n"
+ "  -  Les molettes permettent de pré ou post-composer avec des\n"
+ "     petites rotations selon les axes principaux.\n"
+ "  -  On peut choisir entre 4 modes d'affichage de la rotation.\n"
+ "  -  La touche '?' ou un click droit ouvrent cette fenêtre d'information.\n"
+ "\n" 
+ "Rotator, ©2004 Arnaud Chéritat\n"
);

	// MyHelpApplet prendra en charge la gestion du message
	// d'aide, dont le contenu est défini par setHelpText("contenu");

        setHelpText(auxString.toString());

	//
	setLayout(this);

	// Admin stuff : sans ces appels, les évènements ne sont pas
	// redirigés sur le programme

	addKeyListener(this);
	addMouseListener(this);
	addMouseMotionListener(this);

	/*
	 * Chargement de l'image
	 */
	dePic = getImage(getCodeBase(),"de.png");
	circlePic = getImage(getCodeBase(),"circle.png");
	MediaTracker tracker = new MediaTracker(this);
	tracker.addImage(dePic,0);
	tracker.addImage(circlePic,0);
	try {
	    tracker.waitForAll();
	} catch (InterruptedException e) {}

	/*
	 * Initialisation des variables utilisateurs
	 */

	affMode=0;
	double alpha;
	xV1=new int[nModes];
	yV1=new int[nModes];
	xV2=new int[nModes];
	yV2=new int[nModes];
	for(int i=0; i<nModes; i++) {
	    alpha=2.0*Math.PI*((float) i)/(float) nModes;
	    xV1[i]=(int) (Math.cos(alpha)*(float) R1);
	    yV1[i]=-(int) (Math.sin(alpha)*(float) R1);
	    xV2[i]=(int) (Math.cos(alpha)*(float) R2);
	    yV2[i]=-(int) (Math.sin(alpha)*(float) R2);
	}

	mouseX=0;
	mouseY=0;

	// La fonte
	zeFont=new Font("Courier",Font.PLAIN,16);

	/*
	myBlue=new Color(0,64,255);
	*/
	COL1=new Color(255,128,0);
	COL2=new Color(255,160,160);

	//	setLayout(null);

	// Molettes
	molette=new MyMolette[6];
	for(int i=0;i<6;i++) {
	    molette[i]=new MyMolette(i<3 ? MyMolette.VERTICAL :
			MyMolette.HORIZONTAL,BOX_SIZE-2,BUT_SIZE,10);
	    add(molette[i]);
	    molette[i].addMyMoletteListener(this);
	}

	// Boutons
	butAlea=new Rectangle();
	butAlea.width=dePic.getWidth(this);
	butAlea.height=dePic.getHeight(this);
	butAlea.x=XOFFSET+2*BOX_SEP+3*BOX_SIZE+20;
	butAlea.y=YOFFSET+2*BOX_SEP+3*BOX_SIZE-butAlea.height;

	butTurn=new Rectangle();
	butTurn.width=btSIZE;
	butTurn.height=btSIZE;
	butTurn.x=XOFFSET+2*BOX_SEP+3*BOX_SIZE+29;
	butTurn.y=YOFFSET+BOX_SEP+2*BOX_SIZE-50;

	/*
	 * Initialisation mathématique
	 */

	COS=new double[DIV];
	SIN=new double[DIV];
	for(int i=0; i<DIV; i++) {
	    COS[i]=Math.cos(2.0*Math.PI*((double) i)/(double) DIV);
	    SIN[i]=Math.sin(2.0*Math.PI*((double) i)/(double) DIV);
	}
	COSbis=new double[CIRCDIV];
	SINbis=new double[CIRCDIV];
	for(int i=0; i<CIRCDIV; i++) {
	    COSbis[i]=Math.cos(2.0*Math.PI*((double) i)/(double) CIRCDIV);
	    SINbis[i]=Math.sin(2.0*Math.PI*((double) i)/(double) CIRCDIV);
	}
	memMatr=new double[3][3];
	/*
	    rotation[0][0]=c; rotation[0][1]=-s; rotation[0][2]=0;
	    rotation[1][0]=s; rotation[1][1]=c; rotation[1][2]=0;
	    rotation[2][0]=0; rotation[2][1]=0; rotation[2][2]=1;
	}
	rotMatr=new double[12][3][3];
	int k,l,p,aux;
	for(int n=0;n<12;n++) {
	    for(int i=0;i<3;i++) {
		for(int j=0;j<3;j++) {
		    k=i; l=j;

		    p=(n/2) % 3;
		    if(p==2) {k=i; l=j;}
		    if(p==1) {k=(i+1) % 3; l=(j+1) % 3;}
		    if(p==0) {k=(i+2) % 3; l=(j+2) % 3;}

		    if((n % 2)==1) {aux=k; k=l; l=aux;}

		    rotMatr[n][i][j]=smallRot[k][l];
		}
	    }
	}
	*/

	matrice=new double[3][3];
	auxMatr=new double[3][3];
	axis=new double[3];

	randomMatrix();

    }

    public void compose(int n) {
	double aux[][]=new double[3][3];
	if(n<6) {
	    // composer à gauche
	    for(int i=0;i<3;i++) {
		for(int j=0;j<3;j++) {
		    aux[i][j]=0;
		    for(int k=0;k<3;k++) {
			aux[i][j] += rotMatr[n][i][k]*matrice[k][j];
		    }
		}
	    }
	}
	else {
	    // composer à droite
	    for(int i=0;i<3;i++) {
		for(int j=0;j<3;j++) {
		    aux[i][j]=0;
		    for(int k=0;k<3;k++) {
			aux[i][j] += matrice[i][k]*rotMatr[n-6][k][j];
		    }
		}
	    }
	}

	// recopier le résultat dans 'matrice'
	for(int i=0;i<3;i++) {
	    for(int j=0;j<3;j++) {
		matrice[i][j]=aux[i][j];
	    }
	}
    }

    /*
     * Dessin d'une case de la matrice
     */
    public void myRect(Graphics g, int x, int y, int w, int h) {
	// Remplissage en blanc
	g.fillRect(x, y, w, h);
	// Tracé du bord en noir : remarquer les "-1" en plus
	g.setColor(Color.black);
	g.drawRect(x, y, w-1, h-1);
    }

    /*
     * Pour gérer l'affichage de la class Base,
     * il faut modifier la méthode offPaint
     */
    public void offPaint(Graphics g) {
  
	// Rectangle mathématique en blanc

	storeBounds();

	// Les cases 

	switch(affMode) {
	case 0: {
	    for (int i=0; i<3; i++) {
		for(int j=0; j<3; j++) {
		    int x=XOFFSET+j*(BOX_SIZE+BOX_SEP);
		    int y=YOFFSET+i*(BOX_SIZE+BOX_SEP);
		    g.setColor(Color.white);
		    myRect(g,x,y, BOX_SIZE, BOX_SIZE);
		    g.setFont(zeFont);
		    g.drawString(String.valueOf((int)(matrice[i][j]*100.0)),x+2,y+22);
		}
	    }
	} break;
	case 1: {
	    int y2;
	    for (int i=0; i<3; i++) {
		for(int j=0; j<3; j++) {
		    int x=XOFFSET+j*(BOX_SIZE+BOX_SEP)+BOX_SIZE/4;
		    int y=YOFFSET+i*(BOX_SIZE+BOX_SEP);
		    g.setColor(Color.white);
		    myRect(g,x,y, BOX_SIZE/2, BOX_SIZE);
		    g.setColor(Color.black);
		    y2=-(int) (((float)BOX_SIZE)*matrice[i][j]/2.0);
		    if(y2>0) g.fillRect(x,y+BOX_SIZE/2,BOX_SIZE/2,y2);
		    else g.fillRect(x,y+BOX_SIZE/2+y2,BOX_SIZE/2,-y2);
		}
	    }
	} break;
	case 2: {
	    int xc=XOFFSET+(3*BOX_SIZE)/2+BOX_SEP;
	    int yc=YOFFSET+(3*BOX_SIZE)/2+BOX_SEP;
	    double R=1.5*(float) BOX_SIZE;
	    computeAxis();
	    g.setColor(Color.black);
	    g.drawOval(xc-(int)(1.5*(float) BOX_SIZE),
		       yc-(int)(1.5*(float) BOX_SIZE),
		       3*BOX_SIZE,3*BOX_SIZE);
	    double sign=axis[2]>0 ? 1.0 : -1.0;
	    g.setColor(Color.gray);
	    g.drawLine(xc-(int)(sign*axis[0]*R),yc+(int)(sign*axis[1]*R)
		      ,xc,yc);
	    // Tracé du cercle orthogonal (plan de la rotation)
	    double d=axis[0]*axis[0]+axis[1]*axis[1];
	    if(d>0.0001) { // uniquement s'il n'est pas confondu avec ...
		double h=Math.sqrt(1-d);
		d=Math.sqrt(d);
		double o1,o2,o3;
		o1=-sign*axis[1]/d;
		o2=sign*axis[0]/d;
		int x0,y0,x1,y1;
		x0=xc-(int)(R*o1);
		y0=yc+(int)(R*o2);
		g.setColor(Color.gray);
		for(int i=1; i<CIRCDIV/2; i++) {
		    x1=xc-(int)(R*(o1*COSbis[i]-h*o2*SINbis[i]));
		    y1=yc+(int)(R*(o2*COSbis[i]+h*o1*SINbis[i]));
		    g.drawLine(x0,y0,x1,y1);
		    x0=x1;
		    y0=y1;
		}
		g.setColor(Color.black);
		for(int i=1; i<CIRCDIV/2; i++) {
		    x1=xc+(int)(R*(o1*COSbis[i]-h*o2*SINbis[i]));
		    y1=yc-(int)(R*(o2*COSbis[i]+h*o1*SINbis[i]));
		    g.drawLine(x0,y0,x1,y1);
		    x0=x1;
		    y0=y1;
		}
	    }
	    g.setColor(Color.black);
	    g.drawLine(xc,yc,xc+(int)(sign*axis[0]*R),yc-(int)(sign*axis[1]*R));
	    // Placement des valeurs propres
	    double c=(matrice[0][0]+matrice[1][1]+matrice[2][2]-1.0)/2.0;
	    double s=Math.sqrt(1-c*c);
	    g.fillOval(xc+(int)R-2, yc-2, 5,5);
	    g.fillOval(xc+(int)(R*c)-2, yc-(int)(R*s)-2, 5,5);
	    g.fillOval(xc+(int)(R*c)-2, yc+(int)(R*s)-2, 5,5);

	} break;
	case 3: {
	    int xc=XOFFSET+(3*BOX_SIZE)/2+BOX_SEP;
	    int yc=YOFFSET+(3*BOX_SIZE)/2+BOX_SEP;
	    g.setColor(Color.black);
	    g.drawOval(xc-(int)(1.5*(float) BOX_SIZE),
		       yc-(int)(1.5*(float) BOX_SIZE),
		       3*BOX_SIZE,3*BOX_SIZE);
	    g.setColor(matrice[2][0]>0 ? Color.red :
		       new Color(0.88f,0.6f,0.6f));
	    g.drawLine(xc,yc,xc+(int)(matrice[0][0]*1.5*(float) BOX_SIZE),
		       yc-(int)(matrice[1][0]*1.5*(float) BOX_SIZE));
	    g.setColor(matrice[2][1]>0 ? Color.green :
		       new Color(0.6f,0.85f,0.6f));
	    g.drawLine(xc,yc,xc+(int)(matrice[0][1]*1.5*(float) BOX_SIZE),
		       yc-(int)(matrice[1][1]*1.5*(float) BOX_SIZE));
	    g.setColor(matrice[2][2]>0 ? Color.blue :
		       new Color(0.6f,0.6f,0.9f));
	    g.drawLine(xc,yc,xc+(int)(matrice[0][2]*1.5*(float) BOX_SIZE),
		       yc-(int)(matrice[1][2]*1.5*(float) BOX_SIZE));
	}
	}

	// Les boutons

	/*
	// + et -
	for(int i=0;i<12;i++) {
	    g.setColor(COL2);
	    myRect(g,bouton[i].x,bouton[i].y,bouton[i].width,bouton[i].height);
	    g.drawRect(bouton[i].x+2,bouton[i].y+bouton[i].height/2-1,
		       bouton[i].width-5,1);
	    if((i % 2) == 0) {
		g.drawRect(bouton[i].x+bouton[i].width/2-1,bouton[i].y+2,
			   1,bouton[i].height-5);
	    }
	}
	*/

	// Alea
	g.drawImage(dePic,butAlea.x,butAlea.y,this);
	g.setColor(Color.black);
	g.drawRect(butAlea.x,butAlea.y,butAlea.width-1,butAlea.height-1);
	//	g.setColor(COL1);
	//	myRect(g,butAlea.x,butAlea.y,butAlea.width,butAlea.height);




	// Bouton tournant
	int xc,yc;
	xc=butTurn.x+butTurn.width/2;
	yc=butTurn.y+butTurn.height/2;
	/*
	g.setColor(Color.gray);
	g.fillOval(butTurn.x,butTurn.y,butTurn.width,butTurn.height);
	*/
	g.drawImage(circlePic,butTurn.x-5,butTurn.y-5,this);
	g.setColor(Color.black);
	//	g.drawOval(butTurn.x,butTurn.y,butTurn.width,butTurn.height);
	for(int i=0; i<nModes; i++) {
	    g.fillOval(xc+xV2[i]-1,yc+yV2[i]-1,3,3);
	}
	g.drawLine(xc,yc,xc+xV1[affMode],yc+yV1[affMode]);

	/*	
	xm=but3.x;
	xM=but3.x+but3.width-1;
	ym=but3.y;
	yM=but3.y+but3.height-1;
	g.setColor(Color.white);
	g.fillRect(xm,ym,but3.width,but3.height);
	g.setColor(Color.gray);
	g.drawLine(xm,ym,xM,ym);
	g.drawLine(xm,ym,xm,yM);
	g.setColor(new Color(220,220,220));
	g.drawLine(xM,yM,xM,ym);
	g.drawLine(xM,yM,xm,yM);
	g.setColor(Color.black);
	if(decorations) {
	    g.drawLine(but3.x,but3.y,but3.x+but3.width-1,but3.y+but3.height-1);
	    g.drawLine(but3.x,but3.y+but3.height-1,but3.x+but3.width-1,but3.y);
	}
	*/
	/*
	// L'image texte "Décorations"
	g.drawImage(decorPic,but3.x+but3.width+12,but3.y+3,this);
	*/

	// Les pièces posées


    }

    public boolean testBut(Rectangle but, int x, int y) {
	return(x>but.x && x<but.x+but.width && y>but.y && y<but.y+but.height);
    }

    /*
     *                Implémentation des "interfaces"
     */

    // Interface MouseListener

    public void mouseClicked(MouseEvent e) {
    }
    public void mouseEntered(MouseEvent e) {
	// Do nothing
    }
    public void mouseExited(MouseEvent e) {
	// Do nothing
    }
    public void mousePressed(MouseEvent e) {
	// Do nothing
	    if((e.getModifiers() & InputEvent.SHIFT_MASK )!=0) {
		// En présence de Shift
		int x=e.getX();
		int y=e.getY();
		    clickedX=x;
		    clickedY=y;
	    }
    }
    public void mouseReleased(MouseEvent e) {
	// Testons s'il s'agit du bouton gauche
	if((e.getModifiers() & InputEvent.BUTTON1_MASK)!=0) {
	    // Si oui
	    // Coordonnées du click :
	    int x=e.getX();
	    int y=e.getY();
	    outer:
	    {
		test:
		{
		    if(testBut(butAlea,x,y)) {
			// Click dans le bouton Alea
			randomMatrix();
			break test;
		    }
		    if(testBut(butTurn,x,y)) {
			// Click dans le bouton Alea
			switchTurn();
			break test;
		    }
		    /*
		    for(int i=0; i<12; i++) {
			if(testBut(bouton[i],x,y)) {
			    compose(i);
			    break test;
			}
		    }
		    */
		    break outer;
		}
		repaint();
	    }
	    /*
		else if(x>but1.x && x<but1.x+but1.width 
	             && y>but1.y && y<but1.y+but1.height) {
		    // Click dans le bouton 1
		    zoomOut();
		}
	    */
	}
	/*
	// Ignorons la différence Dragged/Clicked
	mouseClicked(e);
	*/
    }

    // Interface MyMoletteListener

    public void myMoletteMoved(MyMolette s) {
	int k; for(k=0; k<6 && s!=molette[k]; k++);
	int m=s.getMovedBy();
	if(k>=3) {m=-m;}
	double cos=COS[myMod(m,DIV)];
	double sin=SIN[myMod(m,DIV)];
	switch(k) {
	case 0 : {
	    for(int j=0; j<3; j++) {
		matrice[1][j]=cos*memMatr[1][j]-sin*memMatr[2][j];
		matrice[2][j]=sin*memMatr[1][j]+cos*memMatr[2][j];
	    }
	} break;
	case 1 : {
	    for(int j=0; j<3; j++) {
		matrice[2][j]=cos*memMatr[2][j]-sin*memMatr[0][j];
		matrice[0][j]=sin*memMatr[2][j]+cos*memMatr[0][j];
	    }
	} break;
	case 2 : {
	    for(int j=0; j<3; j++) {
		matrice[0][j]=cos*memMatr[0][j]-sin*memMatr[1][j];
		matrice[1][j]=sin*memMatr[0][j]+cos*memMatr[1][j];
	    }
	} break;
	case 3 : {
	    for(int i=0; i<3; i++) {
		matrice[i][1]=cos*memMatr[i][1]-sin*memMatr[i][2];
		matrice[i][2]=sin*memMatr[i][1]+cos*memMatr[i][2];
	    }
	} break;
	case 4 : {
	    for(int i=0; i<3; i++) {
		matrice[i][2]=cos*memMatr[i][2]-sin*memMatr[i][0];
		matrice[i][0]=sin*memMatr[i][2]+cos*memMatr[i][0];
	    }
	} break;
	case 5 : {
	    for(int i=0; i<3; i++) {
		matrice[i][0]=cos*memMatr[i][0]-sin*memMatr[i][1];
		matrice[i][1]=sin*memMatr[i][0]+cos*memMatr[i][1];
	    }
	} break;
	}
	repaint();
    }
    public void myMoletteReleased(MyMolette s) {
	// Do nothing
    }
    public void myMolettePressed(MyMolette s) {
	for(int i=0; i<3; i++) {
	    for(int j=0; j<3; j++) {
		memMatr[i][j]=matrice[i][j];
	    }
	}
	//	whichMolette=s;
    }


    // Interface MouseMotionListener

    public void mouseDragged(MouseEvent e) {
	/*
	int x=e.getX();
	int y=e.getY();
	mouseX=x;
	mouseY=y;
	if(draggingDropZone) {
	    offx=savedoffx+((double)(x-clickedX))/scale;
	    offy=savedoffy-((double)(y-clickedY))/scale;
	    repaint();
	}
	else {
	    // En l'absence de Shift
	    dragging=true;
	}
	*/
	// Ignorons la différence Dragged/Clicked
	mouseMoved(e);
    }
    public void mouseMoved(MouseEvent e) {
	/*
	int x=e.getX();
	int y=e.getY();
	mouseX=x;
	mouseY=y;
	repaint();
	*/
    }

    // Interface KeyListener

    public void keyPressed(KeyEvent e) {
	char c=e.getKeyChar();
	/*
	switch(c) {
	case '+' : zoomIn(); break;
	case '-' : zoomOut(); break;
	}
	    int left=-1;
	    int center=0;
	    int right=1;
	    
	    int turn=center;

	    switch(c) {
	    case 'a' : case 'A' : case 'q' : case 'Q' : 
	    case 'g' : case 'G' : turn=left; break;
	    case 'z' : case 'Z' : case 'w' : case 'W' :
            case 'd' : case 'D' : turn=right; break;
	    }
	    if(e.getKeyCode()==KeyEvent.VK_LEFT) { turn=left; }
	    if(e.getKeyCode()==KeyEvent.VK_RIGHT) { turn=right; }

	    repaint();
	*/

    }
    public void keyReleased(KeyEvent e) {
	// Do nothing
    }
    public void keyTyped(KeyEvent e) {
	// Do nothing
    }

    // Autres

    /*
     * Méthode "abstract" de la classe Base dont dérive cet applet.
     * Gère le Placement des composants en "absolute positioning"
     * Y mettre le code les positionnant --au premier affichage--
     */
    public void position() {
	Dimension d;
	for(int i=0;i<3;i++) {
	    d=molette[i].getPreferredSize();
	    molette[i].setBounds(XOFFSET-BUT_OFF-BUT_SIZE,
                   YOFFSET+i*(BOX_SIZE+BOX_SEP), d.width, d.height);
	    d=molette[3+i].getPreferredSize();
	    molette[3+i].setBounds(XOFFSET+i*(BOX_SIZE+BOX_SEP),
                   YOFFSET-BUT_OFF-BUT_SIZE, d.width, d.height);
	}
    }
    
    /* Cette fonction parle d'elle-même
     * elle n'est pas utilisée par les navigateurs actuels
     */
    public String getAppletInfo() {
         return "Rotator, ©2003 Arnaud Chéritat";
    }
}
