Premessa

          Ultimamente mi sono trovato davanti al problema , usando le SDL, di ruotare una superficie di un angolo x su se stessa. Problema in apparenza banale, mi ha portato ad una lunga ricerca sul web con scarsi risultati. Infatti questo argomento è trattato raramente e, quelle poche volte che viene citato in qualche forum, come risposta si ha sempre di implementare librerie esterne col SDL_GTF o le Open GL.

Ma in questo articolo ho deciso di trascrivervi una funzione, fatta da me, che dovrebbe adempiere a questo compito e senza l'utilizzo di librerie esterne.

Trigonometria

         Il problema si pone più che altro dal punto di vista matematico. 

Dato un piano cartesiano ed un punto su di questo, ruotare il punto attorno all'origine degli assi (il centro del piano) di alfa gradi.

A questo punto, è sufficiente trovare l'angolo della retta punto-centro  con l'asse delle ascisse e sommarvi l'angolo di cui vogliamo ruotare la figura. L'angolo così ottenuto sarà l'ampiezza che intercorre tra la retta punto(ruotato)-centro e l'asse delle ascisse.

Ora dato che ruotando la figura attorno al proprio centro, la distanza tra ogni punto ed il centro del piano non cambia; avendo l'angolo al vertice e la lunghezza dei due lati congruenti di un triangolo isoscele possiamo determinarci le coordinate del punto , oramai ruotato.

Ma come tradurre tutto questo in informatica?

Algoritmo

/*RUOTA IMMAGINE DI ALPHA GRADI*/
void Ruota (SDL_Surface* img,SDL_Surface **dst, double alpha)  
{
       //Dichiarazioni:        
        double dist=0.0;
	double x=0.0,y=0.0;
	double x1=0.0,y1=0.0;
	double beta=0.0;
	int rx=0,ry=0;

	Uint32 Pix;
	//Converto l'angolo in radianti
	alpha=alpha/(180/3.1415926);
        //Ciclo che setaccia tutti i punti
	for (unsigned int i=0; i<=((img->h)*(img->w)); i++)
	{
               //Converto il valore di i in coordinate              
                x=i%img->w;
		y=(int)(i/img->w);
                //Catturo il colore del pixel di coordinate x,y
		Pix=getpixel(img,x,y);

		x=x-(img->w/2);
		y=(img->h/2)-y;
                //Distanza del punto con l'origine degli assi
		dist=sqrt(pow(x,2)+pow(y,2));
		if (dist==0.0)
			dist+=0.001;    //In caso di divisione per 0 il programma si bloccherebbe, pertanto "modifico leggermente dist"
		//A seconda dl quadrante di piano in cui mi trovo, mi calcolo l'angolo punto-centro / asse delle x.
			if ((x>=0)&&(y>=0))
				beta=asin(y/dist);
			if ((x<=0)&&(y>=0))
				beta=3.1415926-asin(y/dist);
			if ((x<=0)&&(y<=0))
				beta=3.1415926+asin(-y/dist);
			if ((x>=0)&&(y<=0))
				beta=6.2831852-asin(-y/dist);
		//Lo incremento dell'angolo di cui ruoto effettivamente la figura.
		beta=beta+alpha;
                //Mi calcolo finalmente le coordinate cartesiane del punto
		x1=cos(beta)*dist;
		y1=sin(beta)*dist;
                //le converto in coordinate pixel
		rx=floor(x1)+(*dst)->w/2;
		ry=(*dst)->h/2-floor(y1);
                //Associo a ogni punto nella nuova posizione il suo colore ricomponendo l'immagine.
		putpixel((*dst),rx,ry,Pix);
	}
}

Gli argomenti della funzione sono:

  • 'img', ovvero la superficie da ruotare,
  • '*dst' ovvero puntatore a superficie; conterrà l'immagine ruotata.
  • 'alpha' angolo (in sessagesimali  ovvero da 0°-360°) di cui si vuole ruotare l'immagine.

Se leggendo i commenti vi siete accorti delle funzioni putpixel e getpixel, sappiate che queste non sono native nelle SDL.

Andrebbero definite prima della funzione ruota-pixel. I sorgenti di queste funzioni sono qui:

http://sdl.beuc.net/sdl.wiki/Pixel_Access

NOTA:

Ruotando l'immagine, per problemi di arrotondamento delle cifre, si vengono a creare dei pixel 'vuoti' . Per rimuoverli in modo abbastanza soddisfacente, è sufficiente impostare la trasparenza dell'immagine al colore di sfondo dell'immagine di destinazione, e disegnare 2 volte l'immagine ruotata, ma spostandola di poco (tipo un pixel).

ruotata=SDL_LoadBMP("Risorse/Bianco.bmp");
Ruota(img,&ruotata, 45);
SDL_SetColorKey(ruotata, SDL_SRCCOLORKEY | SDL_RLEACCEL,SDL_MapRGB(ruotata ->format, 0xff, 0xff, 0xff));  //Trasparenza al bianco
SDL_BlitSurface(ruotata,NULL, screen, &posizione);
posizione.x-=1;
SDL_BlitSurface(ruotata,NULL, screen, &posizione);
posizione.x+=1;

Spero ne possiate fare buon uso e di essere stato abbastanza chiaro.

NOTA:

UNA VERSIONE PIù COMPLETA DELLA GUIDA è DISPONIBILE NEL MIO SITO WEB:

http://danysoftware.altervista.org/rotazioni_sdl.php