Sign in to follow this  

2D rotation

This topic is 4092 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Alright, I know this must have been asked a million times before ( in fact, I even found quite some threads ), but I can't find anything that clarifies things for my particular problem. Visualisation of the problem: As I'm making a small 2D game with SDL I aim to make a rotation function for surfaces in SDL. To do this I use a temporary SDL_Surface to which the rotated surface should be written. From another thread and some googling I found out that to calculate the temporary surface's height and width all I had to do was:
tempwidth = orig_height * sin(angle) + orig_width * cos(angle)
tempheight = orig_height * cos(angle) + orig_width * sin(angle)
So I used this to create the new surface, and the next thing I need to do is to rotate all pixels from the original surface to their rotated positions on the temporary surface. Then, I found that to rotate a single pixel I needed to use the following formula:
x' = cos(theta)*x - sin(theta)*y
y' = sin(theta)*x + cos(theta)*y
It seems, however, that this rotation rotates around the origin (0,0) whereas my surface's 0,0 position is the top left. In short, I'm a bit lost as to how I should actually implement the rotation in my code and I hope someone can help me out. This is my function so far:
void Surface::Rotate ( int _angle )
{
    // Add the rotation angle to the current rotation angle
    rotationAngle += _angle;
    if ( rotationAngle > 360 ) rotationAngle %= 360;

    // Before we can 'create' the surface we must calculate its width and height.
    int newWidth = ceil (  surface->h * sin ( (float)_angle ) + surface->w * cos ( (float) _angle ) );
    int newHeight = ceil ( surface->h * cos ( (float)_angle ) + surface->w * sin ( (float) _angle ) );
	
    // Since the masks in the createsurface function depend on the byte order, we
    // have to check for it.
    #if SDL_BYTEORDER == SDL_BIG_ENDIAN
        Uint32 rmask = 0x00000000;
        Uint32 gmask = 0x00000000;
        Uint32 bmask = 0x00000000;
        Uint32 amask = 0x00000000;
    #else
        Uint32 rmask = 0x00000000;
        Uint32 gmask = 0x00000000;
        Uint32 bmask = 0x00000000;
        Uint32 amask = 0x00000000;
    #endif

    // Create a black surface
    temp = SDL_CreateRGBSurface ( SDL_SWSURFACE, newWidth, newHeight, SCREEN_BPP, rmask, gmask, bmask, amask );

    // Lock the temporary surface so we can write on it
    SDL_LockSurface ( temp );

    // Get pixels and set them in the temporary surface at their newly calculated position
    for ( int i = 0; i < surface->h; i++ )
    {
        for ( int j = 0; j < surface->w; j++ )
        {
            // This is where the implementation should come
            // Help! :P		
        }
    }

    // Writing done, unlock the temporary surface
    SDL_UnlockSurface ( temp );
}


Share this post


Link to post
Share on other sites
To rotate about an arbitrary point, you need to use a conjugate-translation, to temporarily change the origin:

First of all, determine the coordinates of the point you wish to rotate around. Then transform your coordinates so that this point becomes the origin (simple translation), perform the rotation as before, then untransform the coordinates back. This is the common T' = Pˉ¹TP construct. The whole operation would look something like:
// Calulate dimensions of new canvas
int tempwidth = (orig_height * sin(angle) + orig_width * cos(angle));
int tempheight = (orig_height * cos(angle) + orig_width * sin(angle));

// Caculate new origin
int i0 = tempwidth / 2;
int j0 = tempheight / 2;

for (int i = 0; i < tempwidth; ++i) {
for(int j = 0; j < tempheight; ++j) {
float x = (float) (i - i0); // Translate
float y = (float) (j - j0);

float rx = x*cos(-angle) - y*sin(-angle); // Rotate
float ry = x*sin(-angle) + y*cos(-angle);

rx += i0; // Untranslate
ry += j0;

if (rx < 0 || rx >= orig_width || ry < 0 || ry > orig_height) {
reult[i][j] = BACKGROUND_COLOUR;
} else {
result[i][j] = BilinearSample(rx, ry);
}
}
}
I haven't tested this code, so proceed with due scrutiny.

Regards
Admiral

Share this post


Link to post
Share on other sites
i know that:

x' = xcos(theta) - ysin(theta)
y' = xsin(theta) + ycos(theta)

but i'm not sure why it is:

x' = xcos(-theta) - ysin(-theta)
y' = xsin(-theta) + ycos(-theta)

in your code? could you explain please

thanks

Share this post


Link to post
Share on other sites
Quote:
Original post by JasonL220
i know that:

x' = xcos(theta) - ysin(theta)
y' = xsin(theta) + ycos(theta)

but i'm not sure why it is:

x' = xcos(-theta) - ysin(-theta)
y' = xsin(-theta) + ycos(-theta)

in your code? could you explain please

Of course. I should have explained this in the first place.
The only difference between our formulas is that the sign of theta has been swapped. This corresponds to the inverse rotation. This is done because the code works in 'destination space' and calculates the coordinates in 'source space'. Intuitively, if we want to rotate the source by theta to get to the destination, then we'd equivalently need to rotate the destination by -theta to get to the source.
If the code was written the other way - mapping each pixel in the source to the appropriate position in the destination - (which, I'll add, is a bloody mess and should be avoided) then the rotation would go the other way and there's be no need to negate theta.

Regards
Admiral

Share this post


Link to post
Share on other sites

This topic is 4092 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this