Rotacion de imagenes con respecto a un punto en SDL

Consulte acerca de programas, técnicas, algoritmos etc.

Rotacion de imagenes con respecto a un punto en SDL

Notapor carlostex » Mié Ene 20, 2010 11:29 pm

Que tal, pues he desarrollado una función para rotar una imagen con respecto a un punto fijo de la misma, use rotozoom de SDL_gfx para hacer la rotación y luego calculo la posición en la que se debe situar para que quede como debe.

el código es el sig:

Código: Seleccionar todo
/*
* Archivo: rotacion_x_ref.cpp
* Carlos Villanueva
*/

#include<SDL>
#include<math>
#include<SDL>
#include<iostream>
#define PI 3.14159265
using namespace std;

float angulo (int x1, int y1, int x2, int y2)
{
    float ppunto, longv1, longv2;
    ppunto = x1 * x2 + y1 * y2;
    longv1 = sqrt ((x1 * x1) + (y1 * y1));
    longv2 = sqrt ((x2 * x2) + (y2 * y2));
    if (x1 == 0 && y1 == 0)
         return 0;
    else
         return acos (ppunto / (longv1 * longv2));

}

float to_rad(int ang)
{
   return ang*PI/180;
}

float to_ang(float rad)
{
   return rad*180/PI;
}

int mod2 (int x, int y)
{
    return sqrt (x * x + y * y);

}

int dist(int x1, int y1, int x2, int y2)
{
   return mod2(x2-x1,y2-y1);
   
}

Uint32 time_inicio, time_actual;
void reset_time ()
{

    time_inicio = SDL_GetTicks ();
}

Uint32 current_time ()
{
    time_actual = SDL_GetTicks ();
    return time_actual - time_inicio;

}

void rotar_ref(SDL_Surface *src, SDL_Surface *dest, float ang, int pos_x, int pos_y, int x_ref, int y_ref)
{
   
   SDL_Rect punto, dest1, ref;
   SDL_Surface *src_rot;
   src_rot=rotozoomSurface (src, ang, 1, 1);
    float ancho=src->w;
    float alto=src->h;
   
    dest1.y=dest1.x=0;
    punto.x=pos_x;//coordenada donde se sitia el grafico a girar
    punto.y=pos_y;
    
    ref.x=x_ref;//coordenada del punto de referencia con respecto a la imagen
    ref.y=y_ref;
    float largo=mod2(ref.x, ref.y);
    int cuadrante=ang/90;
   
    float phi1, phi2, phi3, rho;
   
   switch(cuadrante)
   {
      case 0:
      //Giro para angulos de 0 a 89
      phi1=to_ang(acos(ref.y/largo))+ang;
      
    punto.y-=ancho*sin(to_rad(ang))-ref.y+largo*cos(to_rad(phi1));
    punto.x-=largo*sin(to_rad(phi1))-ref.x;
      break;
      case 1:
       //giro para angulos de 90 a 180
     phi3=to_ang(acos(ref.x/largo))+180.0-ang;
     rho=to_ang(acos(ref.x/largo));
     punto.x-=ancho*cos(to_rad(180.0-ang))-ref.x-largo*cos(to_rad(phi3));
     punto.y-=ancho*sin(to_rad(180.0-ang))+largo*sin(to_rad(ang-90))-ref.y;
     //punto.y-=(ancho*sin(to_rad(180-ang))-largo*cos(to_rad(rho))-largo*sin(to_rad(180-ang)))*sin(to_rad(180-ang))-ref.y;
     ;
      break;
      
      case 2:
      
       //giro para angulos de 181 a 270
     phi2=to_ang(acos(ref.x/largo))-180.0+ang;
   
     punto.x-=ancho*cos(to_rad(180.0+ang))-ref.x-largo*cos(to_rad(phi2));
     punto.y+=ref.y-largo*sin(to_rad(phi2));
     break;
      case 3:
      //giro para angulos de 271 a 359
      
     phi1=to_ang(atan(ref.x/ref.y))+ang-270.0;
    
     punto.x-=alto*cos(to_rad(ang-270.0))-largo*cos(to_rad(phi1))-ref.x;
     punto.y+=ref.y-largo*sin(to_rad(phi1));
      
   }   
     SDL_BlitSurface(src_rot, NULL, dest, &punto); 
   
}



int main ()
{

    SDL_Surface *image, *screen;

            

    SDL_Event event;
    int done = 0;
   SDL_Rect pos, dest;
   dest.y=dest.x=0;
   dest.w = 640;
    dest.h = 480;
    atexit (SDL_Quit);

    // Iniciar SDL
    if (SDL_Init (SDL_INIT_VIDEO) < 0)
    {
         printf ("No se pudo iniciar SDL: %s\n", SDL_GetError ());
         exit (1);
    }

    // Activamos modo de video
    screen = SDL_SetVideoMode (640, 480, 32, SDL_HWSURFACE);
    if (screen == NULL)
    {
         printf ("No se puede inicializar el modo gráfico: %s\n",
                     SDL_GetError ());
         exit (1);
    }


    // Cargamos gráfico
    image = SDL_LoadBMP ("rect.bmp");//Nombre del archivo de la imagen BMP
    if (image == NULL)
    {
         printf ("No pude cargar gráfico: %s\n", SDL_GetError ());
         exit (1);
    }
   
    //pos.x=pos.y=50;   
    for(float i=0;i<360>format, 0, 0, 0));   
   //SDL_BlitSurface(image, NULL, screen, &pos); 
    rotar_ref(image, screen,i,50,50,0,image->h/2);//imagen a rotar, destino, angulo,posicion en x donde se pondrá, posición en y donde se pondra, coordenadas de referencia a rotar
   
    SDL_Flip(screen);
    while(current_time()<10);
   }



    while (done == 0)
    {
      
      
         while (SDL_PollEvent (&event))
         {
             if (event.type == SDL_KEYDOWN)
                  done = 1;
         }
         
   
    }
}



rotozoom lo que hace es girar la imagen pero la citua de diferentes formas segun el angulo, siempre la pega a la esquina superior izquierda.
Pero creo que tiene algun error las operaciones matematicas, todo va bien, si el punto 'y' de referencia esta a la mitad del alto de la imagen, y 'x' es cero, si lo cambio empieza a haber un cierto movimiento aveces es brusco.
pero solo sucede para angulos de 90 a 179 y de 270 a 360


Espero que me puedan ayudar a corregir el problema o tambien otra solucion que conozcan.


Usé una imagen de un rectangulo para probar, se aprecia mejor

agregar -lSDL -lSDL_gfx en las opciones del compilador
El conocimiento de unos es conocimiento de todos.
Avatar de Usuario
carlostex
 
Mensajes: 249
Registrado: Mar Jul 14, 2009 4:13 am
Ubicación: mexico

Problema solucionado

Notapor carlostex » Jue Ene 21, 2010 9:30 pm

Les comento que ya soluciones el problema, mi error estaba en el enfoque que seguí, pues lo que hacia en el código anterior era localizar a que distancia se encuentra el punto de referencia de la imagen rotada. y por la forma en la que la función que rota pone la imagen en la superficie el problema carece de simetría.
Pero vi en otro hilo que alguien quería hacer lo mismo, y mencionaba que el único punto mas fácil de obtener es el centro, luego mover el centro al lugar que se desee, pues seguí esa idea base, y en cuestión de minutos ya tenia la función lista, como verán es mas elegante y simple.

Código: Seleccionar todo
/*
* Archivo: rotacion_x_ref.cpp
* Carlos Villanueva
*/

#include<SDL/SDL.h>


#include<math.h>
#include<SDL/SDL_rotozoom.h>
#include<iostream>
#define PI 3.14159265
using namespace std;

float angulo (int x1, int y1, int x2, int y2)
{
    float ppunto, longv1, longv2;
    ppunto = x1 * x2 + y1 * y2;
    longv1 = sqrt ((x1 * x1) + (y1 * y1));
    longv2 = sqrt ((x2 * x2) + (y2 * y2));
    if (x1 == 0 && y1 == 0)
         return 0;
    else
         return acos (ppunto / (longv1 * longv2));

}

float to_rad(int ang)
{
   return ang*PI/180;
}

float to_ang(float rad)
{
   return rad*180/PI;
}

Uint32 time_inicio, time_actual;
void reset_time ()
{

    time_inicio = SDL_GetTicks ();
}

Uint32 current_time ()
{
    time_actual = SDL_GetTicks ();
    return time_actual - time_inicio;

}

void rotar_ref(SDL_Surface *src, SDL_Surface *dest, float ang, int pos_x, int pos_y, int x_ref, int y_ref)
{
   
   SDL_Rect punto;
   SDL_Surface *src_rot;
   src_rot=rotozoomSurface (src, ang, 1, 1);
   float ancho=src->w;
   
   float largo_bar=ancho/2-x_ref;
   
   punto.x=pos_x;//coordenada donde se situa el punto de referencia
   punto.y=pos_y;
    
   punto.x=punto.x-src_rot->w/2+largo_bar*cos(to_rad(ang));
   punto.y=punto.y-src_rot->h/2-largo_bar*sin(to_rad(ang));
      
   SDL_BlitSurface(src_rot, NULL, dest, &punto); 
   
}



int main ()
{

    SDL_Surface *image, *screen;

            

    SDL_Event event;
    int done = 0;
   SDL_Rect pos, dest, eje;
   dest.y=dest.x=0;
   dest.w = 640;
    dest.h = 480;
    atexit (SDL_Quit);

    // Iniciar SDL
    if (SDL_Init (SDL_INIT_VIDEO) < 0)
    {
         printf ("No se pudo iniciar SDL: %s\n", SDL_GetError ());
         exit (1);
    }

    // Activamos modo de video
    screen = SDL_SetVideoMode (640, 480, 32, SDL_HWSURFACE);
    if (screen == NULL)
    {
         printf ("No se puede inicializar el modo gráfico: %s\n",
                     SDL_GetError ());
         exit (1);
    }


    // Cargamos gráfico
    image = SDL_LoadBMP ("canyon.bmp");
    if (image == NULL)
    {
         printf ("No pude cargar gráfico: %s\n", SDL_GetError ());
         exit (1);
    }
   
   
   
    eje.x=4;//Puntos de referencia en la imagen
    eje.y=8;
   for (float i=0;i<=360.0;i+=1.0){
    reset_time();
    pos.x=100;//posicion donde se imprimira la imagen de prueba sin rotacion
    pos.y=100;
    pos.x-=eje.x;
    pos.y-=eje.y;    
    SDL_FillRect (screen, &dest, SDL_MapRGB (screen->format, 0, 0, 0));   
    SDL_BlitSurface(image, NULL, screen, &pos); 
    rotar_ref(image, screen,i,100,100,eje.x,eje.y);//repetir aca las coordenadas donde se imprimira
   
    SDL_Flip(screen);
    while(current_time()<40);
   }



    while (done == 0)
    {
      
      
         while (SDL_PollEvent (&event))
         {
             if (event.type == SDL_KEYDOWN)
                  done = 1;
         }
         
   
    }
}



Pueden usar cualquier imagen, espero le sirva a alguien, haora con esto voy hacer una implementacion en C++ y SDL parecida a artillery
El conocimiento de unos es conocimiento de todos.
Avatar de Usuario
carlostex
 
Mensajes: 249
Registrado: Mar Jul 14, 2009 4:13 am
Ubicación: mexico


Volver a General

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 1 invitado