Il suffit d'insérer dans la page < script type="text/javascript" src="son_emplacement\ASCIIMathML.js" ></script > pour que les parties entre \$ ...\$ ou \`...\` soient interprètées comme des commandes mathématiques et présentées comme telles.
la méthode d'Euler consiste à calculer une solution approchée pas `t_i` à pas `t_{i+1}=t_i+h` (longueur du pas : `h`), en approchant entre chaque pas la fonction `f` par sa valeur en `(y(t_i))
`y'` étant constant égale à `f(y(t_i))`, on a alors `y(t_i+h)=y(t_i)+h.f(y(t_i))`
de façon plus imagée, si la vitesse `v` est constante, en un temps `h` la position se sera déplacée de `h.v`.
on calcule donc de proche en proche par `y_{suivant}=y_{précédent}+h.f(y_{précédent})` ou en notation mathématiques `y_{i+1}=y_{i}+h.f(y_{i})`
#include <SDL.h> #include <stdlib.h> #include <stdio.h> #include <math.h> #define HAUTEUR 800 #define LARGEUR 800 #define NB_ASTRES_MAX 10 #define PAS 0.001 #define RAFRAICHISSEMENT 20 #define G 0.5 #define NB_PAS 1 typedef struct vecteur { float x,y,z; } vecteur; typedef struct derivees { vecteur vitesse; vecteur acceleration; } derivee; void affecte(vecteur u,vecteur *v) { v->x=u.x; v->y=u.y; v->z=u.z; } void diff(vecteur v1, vecteur v2, vecteur * v3) { v3->x=v1.x-v2.x; v3->y=v1.y-v2.y; v3->z=v1.z-v2.z; //printf("difference %f %f %f:\n",v3->x,v3->y,v3->z); } void somme(vecteur v1, vecteur v2, vecteur * v3) { v3->x=v1.x+v2.x; v3->y=v1.y+v2.y; v3->z=v1.z+v2.z; } // fait le produit sur place void produit(float alpha,vecteur src,vecteur *dst) { dst->x=alpha*src.x; dst->y=alpha*src.y; dst->z=alpha*src.z; }; float prodScal(vecteur u, vecteur v) { return u.x*v.x+u.y*v.y+u.z*v.z; } float norme(vecteur v) { return sqrt(prodScal(v,v)); } float distCarre(vecteur v1, vecteur v2) { vecteur v3; diff( v1,v2,&v3); return norme(v3); } float dist(vecteur v1, vecteur v2) { return sqrt(distCarre(v1,v2)); } float cosalpha=sqrt(3)/2, sinalpha=0.5; void afficheVecteur(vecteur u) { printf("%f %f %f ",u.x,u.y,u.z); } // rotation de (u,v) d'un angle alpha void rotation(float cosalpha,float sinalpha,vecteur *u,vecteur *v) { vecteur tampon,tampon1,tampon2; printf("norme de u %f et de v %f avant \n",norme(*u),norme(*v)); produit(cosalpha,*u,&tampon1); produit(sinalpha,*v,&tampon2); somme(tampon1,tampon2,&tampon); produit(-sinalpha,*u,&tampon1); produit(cosalpha,*v,&tampon2); somme(tampon1,tampon2,v); affecte(tampon,u); printf("norme de u %f et de v %f apres \n",norme(*u),norme(*v)); } typedef struct astre { vecteur position; vecteur vitesse; float masse; Uint32 couleur; } astre; int initialiseLesAstres(astre * astres) { FILE *fichier=NULL; fichier = fopen("lesastres.txt", "r"); if (!fichier) { printf("ouverture de lesastres.txt impossible\n"); } printf("ouverture de lesastres.txt correcte\n"); int n=NB_ASTRES_MAX; fscanf(fichier, "%d", &n); printf("nombre d'astres : %d\n", n); if (n>NB_ASTRES_MAX) { n=NB_ASTRES_MAX; } printf("nombre d'astres rectifie 2 : %d\n", n); int i=0; for (i=0;i<n;i++) { fscanf(fichier, "%f %f %f %f %f %f %f ", &astres[i].position.x ,&astres[i].position.y,&astres[i].position.z, &astres[i].vitesse.x,&astres[i].vitesse.y,&astres[i].vitesse.z, &astres[i].masse); }; fclose(fichier); return n; } // la couleur est proportionnelle à la masse du rouge au turquoise void initialiseCouleurs(SDL_Surface *ecran,int n,astre *astres) { int i=0; // détermine la masse maximale float m=0; for (i=0;i<n;i++) { if(astres[i].masse>m ){ m=astres[i].masse ;} } //printf("masse maximale : %f\n",m); // affecte les couleurs porportionellement Uint8 r=0,b=0,v=0; for (i=0;i<n;i++) { r=Uint8((1-astres[i].masse/m)*255); b=Uint8(astres[i].masse/m*255); v=b; printf("couleur de %d : (rouge %d vert %d bleu %d)\n",i,r,v,b); //Uint32 couleur=SDL_MapRGB(ecran->format,r,v,b); //printf("OK ?\n"); astres[i].couleur=SDL_MapRGB(ecran->format,r,v,b); printf("couleur de %d : Uint32 %d)\n",i,astres[i].couleur); } } void fctBarycentre(int n, astre *astres, astre *barycentre) { int i; float mTotale=0,m,x=0,y=0,z=0,vx=0,vy=0,vz=0; for (i=0;i<n;i++) { m=astres[i].masse; mTotale+=m; x+=astres[i].position.x*m; y+=astres[i].position.y*m; z+=astres[i].position.z*m; vx+=astres[i].vitesse.x*m; vy+=astres[i].vitesse.y*m; vz+=astres[i].vitesse.z*m; }; barycentre->position.x=x/mTotale; barycentre->position.y=y/mTotale; barycentre->position.z=z/mTotale; barycentre->vitesse.x=vx/mTotale; barycentre->vitesse.z=vy/mTotale; barycentre->vitesse.z=vz/mTotale; } // projection orthogonale sur (centre,u,v) void projection(vecteur centre,vecteur u, vecteur v, astre astreSrc, vecteur *dst) { vecteur CA; // centre->astre diff(astreSrc.position,centre,&CA); // coordonnées sur u et v dst->x=prodScal(u,CA); dst->y=prodScal(v,CA); } // facteur d'echelle pour que tout rentre dans l'écran float fctEchelle(int n,astre * astres, vecteur centre,vecteur u,vecteur v) { float xMin=0,yMin=0,xMax=0,yMax=0,coef=1; int i=0; vecteur tampon; for (i=0;i<n;i++) { projection(centre,u,v,astres[i],&tampon); if (tampon.x<xMin) {xMin=tampon.x;} if (tampon.x>xMax) {xMax=tampon.x;} if (tampon.y<yMin) {yMin=tampon.y;} if (tampon.y>yMax) {yMax=tampon.y;} }; // détermine le plus grand écart relatif coef=fabs(xMin)/LARGEUR; if(fabs(xMax)/LARGEUR>coef) {coef=fabs(xMax)/LARGEUR;} if(fabs(yMin)/HAUTEUR>coef) {coef=fabs(yMin)/HAUTEUR;} if(fabs(yMax)/HAUTEUR>coef) {coef=fabs(yMax)/HAUTEUR;} if (coef==0) { return 1; } else { return 0.5/coef; } } //colle les astres sur "ecran" après projection et mise à l'échelle void afficheAstres(SDL_Surface *ecran,vecteur centre,vecteur u,vecteur v,float echelle, int n, astre * astres) //////////////////// /////////////////// // il faut projeté avant de détermine rle facteur d'échelle ! { // noirci le ciel // pour chaque astre vecteur dst; int i=0; for (i=0;i<n;i++) { // calcule le projeté projection(centre,u,v,astres[i], &dst); // met à l'échelle produit(echelle,dst, &dst); //printf( "astre %d projete sur : %f %f \n",i,dst.x,dst.y); dst.x+=LARGEUR*0.5; dst.y+=HAUTEUR*0.5; SDL_Rect rect; rect.x=dst.x; rect.y=dst.y; rect.w=2; rect.h=2; SDL_FillRect(ecran,&rect,astres[i].couleur); } SDL_Flip(ecran); } // détermine le numéro de l'astre cliqué et -1 s'il n'y en a pas int astreClique(SDL_Surface *ecran,int clicX,int clicY,int n,astre *astres,vecteur centre,vecteur u, vecteur v,float echelle) { vecteur dst; int i=0; for (i=0;i<n;i++) { // calcule le projeté projection(centre,u,v,astres[i], &dst); // met à l'échelle produit(echelle,dst, &dst); dst.x+=LARGEUR*0.5; dst.y+=HAUTEUR*0.5; if (fabs(dst.x-clicX)<4 && fabs(dst.y-clicY)<4) { SDL_Rect rect; rect.x=dst.x-2; rect.y=dst.y-2; rect.w=4; rect.h=4; SDL_FillRect(ecran,&rect,SDL_MapRGB(ecran->format,255,255,255)); SDL_Flip(ecran); return i; } } return -1; } int initialiseSDLVideo(SDL_Surface *ecran){ if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) { printf( "erreur d'iinitialisation video: %s\n", SDL_GetError() ); return 1; } //s'assure de la fermeture propre //atexit(SDL_Quit); // creé la fenetre ecran = SDL_SetVideoMode(LARGEUR, HAUTEUR, 32, SDL_HWSURFACE|SDL_DOUBLEBUF); if ( !ecran ) { printf("impossible de crerer la fenetre: %s\n", SDL_GetError()); return 2; } return 0; }; void attraction(int n,astre *astres,int numAstre,vecteur *force) { force->x=0; force->y=0; force->z=0; float d=0,f=0; int i=0; vecteur v; for (i=0;i<n;i++) { if (i!=numAstre) { diff(astres[i].position,astres[numAstre].position,&v); d=norme(v); //printf("distance %d %d : %f \n",i,numAstre,d); if (d>0) { f=G*astres[i].masse/(d*d*d); produit(f,v,&v); somme(v,*force,force); } } } } void suivantRK4(int n,astre *astres) { derivees k0[NB_ASTRES_MAX]; derivees k1[NB_ASTRES_MAX]; derivees k2[NB_ASTRES_MAX]; derivees k3[NB_ASTRES_MAX]; astre astresTampon[NB_ASTRES_MAX]; vecteur vTampon,vSomme; //vecteur acceleration; int i=0; // méthode de Rounge-Kutta d'ordre 4: // pour la relation y'(t)=f(y(t)) et pour un pas h // position'=vitesse // vitesse'=accélération=attraction (position) // ici position'=vitesse et vitesse'=attraction(...) // on définit successivement // en partant de l'état y0 vers l'état y1 : // k0=f(y0) // k1=f(y0+h/2*k0) // k2=f(y0+h/2*k1) // k3= f(y0+h*k2) // y1=y0+h*(k0+2k1+2k2+k3)/6 // k0=f(y0) for (i=0;i<n;i++) { k0[i].vitesse.x=astres[i].vitesse.x; k0[i].vitesse.y=astres[i].vitesse.y; k0[i].vitesse.z=astres[i].vitesse.z; attraction(n,astres,i,&(k0[i].acceleration)); } // y0+h/2*k0 dans astresTampon for (i=0;i<n;i++) { //calcule y0+h/2*k0 //calcule h/2*k0 //positions produit(PAS/2,k0[i].vitesse,&vTampon); somme(astres[i].position,vTampon,&(astresTampon[i].position)); //masse astresTampon[i].masse=astres[i].masse; //vitesses produit(PAS/2,k0[i].acceleration,&vTampon); somme(astres[i].vitesse,vTampon,&( k1[i].vitesse)); } // acceleration for (i=0;i<n;i++) { attraction(n,astresTampon,i,&(k1[i].acceleration)); } // k2=f(y0+h/2*k1) for (i=0;i<n;i++) { //calcule y0+h/2*k0 //calcule h/2*k0 //positions produit(PAS/2,k1[i].vitesse,&vTampon); somme(astres[i].position,vTampon,&(astresTampon[i].position)); //masse astresTampon[i].masse=astres[i].masse; //vitesses produit(PAS/2,k1[i].acceleration,&vTampon); somme(astres[i].vitesse,vTampon,&( k2[i].vitesse)); } // acceleration for (i=0;i<n;i++) { attraction(n,astresTampon,i,&(k2[i].acceleration)); } // k3=f(y0+h*k2) //printf("k3 \n"); for (i=0;i<n;i++) { //calcule y0+h*k0 //calcule h*k0 //positions produit(PAS,k2[i].vitesse,&vTampon); somme(astres[i].position,vTampon,&(astresTampon[i].position)); //masse astresTampon[i].masse=astres[i].masse; //vitesses produit(PAS/2,k2[i].acceleration,&vTampon); somme(astres[i].vitesse,vTampon,&( k3[i].vitesse)); } // acceleration for (i=0;i<n;i++) { attraction(n,astresTampon,i,&(k3[i].acceleration)); } // y1=y0+h*(k0+2k1+2k2+k3)/6 for (i=0;i<n;i++) { // k0+2k1+2k2+k3 produit(2,k1[i].vitesse,&vTampon); somme(k0[i].vitesse,vTampon,&vSomme); produit(2,k2[i].vitesse,&vTampon); somme(vSomme,vTampon,&vSomme); somme(vSomme,k3[i].vitesse,&vSomme); // h/6*(k0+2k1+2k2+k3) produit(PAS/6,vSomme,&vSomme); //y1=y0+h/6*(k0+2k1+2k2+k3) somme(astres[i].position,vSomme,&(astres[i].position)); produit(2,k1[i].acceleration,&vTampon); somme(k0[i].acceleration,vTampon,&vSomme); produit(2,k2[i].acceleration,&vTampon); somme(vSomme,vTampon,&vSomme); somme(vSomme,k3[i].acceleration,&vSomme); produit(PAS/6,vSomme,&vSomme); somme(astres[i].vitesse,vSomme,&(astres[i].vitesse)); } } void suivantEuler(int n,astre *astres) { astre astresSuivants[NB_ASTRES_MAX]; vecteur acceleration,tampon; int i=0; for (i=0;i<n;i++) { produit(PAS,astres[i].vitesse,&tampon); somme(tampon,astres[i].position,&(astresSuivants[i].position)); attraction(n,astres,i,&acceleration); produit(PAS,acceleration,&acceleration); somme(astres[i].vitesse,acceleration,&(astresSuivants[i].vitesse)); } for (i=0;i<n;i++) { astres[i].position.x=astresSuivants[i].position.x; astres[i].position.y=astresSuivants[i].position.y; astres[i].position.z=astresSuivants[i].position.z; astres[i].vitesse.x=astresSuivants[i].vitesse.x; astres[i].vitesse.y=astresSuivants[i].vitesse.y; astres[i].vitesse.z=astresSuivants[i].vitesse.z; } } // calcule la nouvelle position suivant le pas de la méthode RK4 // avec nbPas par RAFRAICHISSEMENT (1/20ème de seconde ) // signale s'il n'y arrive pas // et modifie l'affichage // le centre est repéré par un numéro : -1 pour le barycenter, et le numéro de l'astre sinon. void evolution(SDL_Surface *ecran,int n,astre *astres, int *numCentre,vecteur *u, vecteur *v,vecteur *w,float *echelle,int *nbPas) { vecteur centre; astre barycentre; bool trace=true; if (*numCentre==-1) { fctBarycentre(n,astres,&barycentre); affecte(barycentre.position,¢re); } else { affecte(astres[*numCentre].position,¢re); } printf("centre position.x %f %f %f\n",centre.x,centre.y,centre.z); int nb=0,i=0; // nombre de pas effectués SDL_Event event; bool poursuit=true,pause=false,efface=false; Uint32 temps=SDL_GetTicks(); int numAstre=0; int clicX=0, clicY=0; // avant chaque calcul de positoin, vérifie les événements et // si le temps est passé, le signale. while (poursuit) { // gere les interractions clavier if( SDL_PollEvent(&event)) { switch(event.type) { case SDL_QUIT : // Si c'est un évènement de type "Quitter" poursuit=false; break; case SDL_MOUSEBUTTONDOWN: clicX=event.button.x, clicY=event.button.y; numAstre=astreClique(ecran,clicX,clicY,n,astres, centre,*u,*v,*echelle); // changement de centre if (event.button.button==SDL_BUTTON_LEFT) { *numCentre=numAstre; SDL_FillRect(ecran,NULL,SDL_MapRGB(ecran->format, 0,0,0)); } // efface l'étoile sélectionnée (couleur noir, poids nul) if (event.button.button==SDL_BUTTON_RIGHT) { efface=false; if (numAstre>-1) { astres[numAstre].masse=0; astres[numAstre].couleur=0; } } break; case SDL_KEYDOWN : // une touche clavier est enfoncée switch(event.key.keysym.sym) { case SDLK_q: // a en clavier azerty : ajuste et recentre la fenetre *echelle=fctEchelle(n,astres,centre,*u,*v)*0.9; for (i=0;i<n;i++) { diff(astres[i].position,centre,&(astres[i].position)); } printf("recentrage sur le centre\n"); SDL_FillRect(ecran,NULL,SDL_MapRGB(ecran->format, 0,0,0)); break; case SDLK_t: // active/désactive la trace trace=!trace; break; case SDLK_KP_PLUS: // zoom avant SDL_FillRect(ecran,NULL,SDL_MapRGB(ecran->format, 0,0,0)); *echelle=*echelle*2; printf("zoom avant\n"); break; case SDLK_KP_MINUS: // zoom arrière //SDL_FillRect(ecran,NULL,SDL_MapRGB(ecran->format, 0,0,0)); *echelle=*echelle/=2; printf("zoom arrière \n"); break; case SDLK_DOWN: // rotation autour de u //SDL_FillRect(ecran,NULL,SDL_MapRGB(ecran->format, 0,0,0)); rotation(cosalpha,sinalpha,v,w); break; case SDLK_KP2: // rotation autour de u //SDL_FillRect(ecran,NULL,SDL_MapRGB(ecran->format, 0,0,0)); rotation(cosalpha,sinalpha,v,w); break; case SDLK_UP : // rotation autour de u //SDL_FillRect(ecran,NULL,SDL_MapRGB(ecran->format, 0,0,0)); rotation(cosalpha,-sinalpha,v,w); break; case SDLK_KP8: // rotation autour de u //SDL_FillRect(ecran,NULL,SDL_MapRGB(ecran->format, 0,0,0)); rotation(cosalpha,-sinalpha,v,w); break; case SDLK_RIGHT: // rotation autour de v //SDL_FillRect(ecran,NULL,SDL_MapRGB(ecran->format, 0,0,0)); rotation(cosalpha,sinalpha,u,w); break; case SDLK_KP6: // rotation autour de v //SDL_FillRect(ecran,NULL,SDL_MapRGB(ecran->format, 0,0,0)); rotation(cosalpha,sinalpha,u,w); break; case SDLK_LEFT: // rotation autour de v //SDL_FillRect(ecran,NULL,SDL_MapRGB(ecran->format, 0,0,0)); rotation(cosalpha,-sinalpha,u,w); break; case SDLK_KP4: // rotation autour de v //SDL_FillRect(ecran,NULL,SDL_MapRGB(ecran->format, 0,0,0)); rotation(cosalpha,-sinalpha,u,w); break; case SDLK_p: // pause attend le retour d'un p pause=!pause; break; case SDLK_r: // plus rapide *nbPas*=2; break; case SDLK_l: // plus lent *nbPas/=2; *nbPas+=1; break; case SDLK_ESCAPE: // recentre et ajuste la fenetre (clavier qwerty) poursuit=false; break; default: break; printf("autre touche\n"); break; } break; } } if (!poursuit) { break; } // vérifie que le nombre depas n'est pas excessif if (!pause) { if (SDL_GetTicks()>temps+RAFRAICHISSEMENT) { printf("nombre de pas par rafraichissement réalisé %d pour %d\n",nb,*nbPas); temps=SDL_GetTicks(); } // si le nombre de pas est effectué, attend le temps d'affichage if (nb>*nbPas) { nb=0; //printf("attend la fin du dé&lai avant affichage\n"); if (*numCentre==-1) { fctBarycentre(n,astres,&barycentre); affecte(barycentre.position,¢re); } else { affecte(astres[*numCentre].position,¢re); } if (trace) { SDL_FillRect(ecran,NULL,SDL_MapRGB(ecran->format, 0,0,0)); } afficheAstres(ecran,centre,*u,*v,*echelle,n,astres); while(SDL_GetTicks()<temps+20){}; temps=SDL_GetTicks(); } else { suivantRK4(n,astres); nb++; } } } } int main ( int argc, char** argv ) { // initialisation de l'écran SDL_Surface *ecran; ecran = SDL_SetVideoMode(LARGEUR, HAUTEUR, 32, SDL_HWSURFACE|SDL_DOUBLEBUF); if ( !ecran ) { printf("impossible de crerer la fenetre: %s\n", SDL_GetError()); return 2; } atexit(SDL_Quit); printf("ecran cree \n"); astre astres[NB_ASTRES_MAX]; printf("creation des astres tres correcte\n"); int n=NB_ASTRES_MAX; n=initialiseLesAstres(astres); printf("initialisation des astres correcte\n"); initialiseCouleurs(ecran, n,astres); printf("initialisation des couleurs correcte\n"); astre barycentre; fctBarycentre(n,astres, &barycentre); float coef=1; vecteur u={1,0,0}; vecteur v={0,1,0}; vecteur w={0,0,1}; coef=fctEchelle(n,astres, barycentre.position,u,v)*0.9; printf("coef %f \n",coef); afficheAstres(ecran,barycentre.position,u,v,coef,n,astres); int nbPas=NB_PAS;int numCentre=-1; evolution(ecran,n,astres, &numCentre,&u,&v,&w,&coef,&nbPas); SDL_FreeSurface(ecran); // all is well ;) printf("Sortie correcte\n"); return 0; }