views:

109

answers:

3

I am new to python and graphics but have programmed before. According to http://en.wikipedia.org/wiki/Transformation_matrix#Rotation ,

For rotation by an angle θ anticlockwise about the origin, the functional form is x' = xcosθ − ysinθ and y' = xsinθ + ycosθ

But the following python code rotates it in the clockwise direction. Could somebody explain this?. Also translating the rectangle to the origin and back to the center seems to be an overhead. Is there any way to avoid this?. Thanks in advance.

PS: I have looked at pygame.transform.rotate which does this but I would like to start from scratch to get better idea about the graphics. Is there a way to see the source of this method from python interpreter?

import pygame, sys, time
from math import *
from pygame.locals import *
co_ordinates =((200,200),(400,200),(400,300),(200,300))

window_surface = pygame.display.set_mode((500,500),0,32)
BLACK=(0,0,0)
GREEN=(0,255,0)
RED=(255,0,0)
window_surface.fill(BLACK)
ang=radians(30)
"""orig=pygame.draw.polygon(window_surface,GREEN,co_ordinates)
n_co_ordinates = tuple([(((x[0])*cos(ang)-(x[1])*sin(ang)),((x[0])*sin(ang)+(x[1])*cos(ang))) for x in n_co_ordinates])
n_co_ordinates = tuple([((x[0]+300),(x[1]+250)) for x in n_co_ordinates])
print(n_co_ordinates)
pygame.draw.polygon(window_surface,RED,n_co_ordinates)"""


pygame.display.update()
while True:
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
    for i in range(360):
        ang=radians(i)
        if i>=360:
            i=0
        n_co_ordinates = tuple([((x[0]-300),(x[1]-250)) for x in co_ordinates])
        n_co_ordinates = tuple([((x[0]*cos(ang)-x[1]*sin(ang)),(x[0]*sin(ang)+x[1]*cos(ang))) for x in n_co_ordinates])
        n_co_ordinates = tuple([((x[0]+300),(x[1]+250)) for x in n_co_ordinates])
        window_surface.fill(BLACK)
        pygame.draw.polygon(window_surface,RED,n_co_ordinates)
        pygame.display.update()
        time.sleep(0.02)
A: 

Change the signs on the sine terms, like this:

n_co_ordinates = tuple([(((x[0])*cos(ang)+(x[1])*sin(ang)),((-x[0])*sin(ang)+(x[1])*cos(ang))) for x in n_co_ordinates]) 

See if that helps.

duffymo
+2  A: 

To rotate in the opposite direction change ang to -ang. I suspect you have got a sign wrong in the rotation matrix, but I can never remember. (EDIT: This is equivalent to changing the sign of the sin terms, because sin(-x)==-sin(x) and cos(-x)==cos(x).)

You can't avoid the translation to the centre. The reason is that your transformation fixes the origin (0,0) (since 0*cos(...)==0), so you are always rotating about the origin. Thus, to rotate about anywhere else, you have to translate that point to the origin first.

Here is the source of rotate, from transform.c in the pygame source. It's written in C.

static void
rotate (SDL_Surface *src, SDL_Surface *dst, Uint32 bgcolor, double sangle,
        double cangle)
{
    int x, y, dx, dy;

    Uint8 *srcpix = (Uint8*) src->pixels;
    Uint8 *dstrow = (Uint8*) dst->pixels;
    int srcpitch = src->pitch;
    int dstpitch = dst->pitch;

    int cy = dst->h / 2;
    int xd = ((src->w - dst->w) << 15);
    int yd = ((src->h - dst->h) << 15);

    int isin = (int)(sangle * 65536);
    int icos = (int)(cangle * 65536);

    int ax = ((dst->w) << 15) - (int)(cangle * ((dst->w - 1) << 15));
    int ay = ((dst->h) << 15) - (int)(sangle * ((dst->w - 1) << 15));

    int xmaxval = ((src->w) << 16) - 1;
    int ymaxval = ((src->h) << 16) - 1;

    switch (src->format->BytesPerPixel)
    {
    case 1:
        for (y = 0; y < dst->h; y++)
        {
            Uint8 *dstpos = (Uint8*)dstrow;
            dx = (ax + (isin * (cy - y))) + xd;
            dy = (ay - (icos * (cy - y))) + yd;
            for (x = 0; x < dst->w; x++)
            {
                if(dx < 0 || dy < 0 || dx > xmaxval || dy > ymaxval)
                    *dstpos++ = bgcolor;
                else
                    *dstpos++ = *(Uint8*)
                        (srcpix + ((dy >> 16) * srcpitch) + (dx >> 16));
                dx += icos;
                dy += isin;
            }
            dstrow += dstpitch;
        }
        break;
    case 2:
        for (y = 0; y < dst->h; y++)
        {
            Uint16 *dstpos = (Uint16*)dstrow;
            dx = (ax + (isin * (cy - y))) + xd;
            dy = (ay - (icos * (cy - y))) + yd;
            for (x = 0; x < dst->w; x++)
            {
                if (dx < 0 || dy < 0 || dx > xmaxval || dy > ymaxval)
                    *dstpos++ = bgcolor;
                else
                    *dstpos++ = *(Uint16*)
                        (srcpix + ((dy >> 16) * srcpitch) + (dx >> 16 << 1));
                dx += icos;
                dy += isin;
            }
            dstrow += dstpitch;
        }
        break;
    case 4:
        for (y = 0; y < dst->h; y++)
        {
            Uint32 *dstpos = (Uint32*)dstrow;
            dx = (ax + (isin * (cy - y))) + xd;
            dy = (ay - (icos * (cy - y))) + yd;
            for (x = 0; x < dst->w; x++)
            {
                if (dx < 0 || dy < 0 || dx > xmaxval || dy > ymaxval)
                    *dstpos++ = bgcolor;
                else
                    *dstpos++ = *(Uint32*)
                        (srcpix + ((dy >> 16) * srcpitch) + (dx >> 16 << 2));
                dx += icos;
                dy += isin;
            }
            dstrow += dstpitch;
        }
        break;
    default: /*case 3:*/
        for (y = 0; y < dst->h; y++)
        {
            Uint8 *dstpos = (Uint8*)dstrow;
            dx = (ax + (isin * (cy - y))) + xd;
            dy = (ay - (icos * (cy - y))) + yd;
            for (x = 0; x < dst->w; x++)
            {
                if (dx < 0 || dy < 0 || dx > xmaxval || dy > ymaxval)
                {
                    dstpos[0] = ((Uint8*) &bgcolor)[0];
                    dstpos[1] = ((Uint8*) &bgcolor)[1];
                    dstpos[2] = ((Uint8*) &bgcolor)[2];
                    dstpos += 3;
                }
                else
                {
                    Uint8* srcpos = (Uint8*)
                        (srcpix + ((dy >> 16) * srcpitch) + ((dx >> 16) * 3));
                    dstpos[0] = srcpos[0];
                    dstpos[1] = srcpos[1];
                    dstpos[2] = srcpos[2];
                    dstpos += 3;
                }
                dx += icos; dy += isin;
            }
            dstrow += dstpitch;
        }
        break;
    }
}
katrielalex
Thanks a lot. Negating the angle works but shouldn't that work without doing that acc to wiki?.
pyguy12
Huh. Yes, I think it should rotate counterclockwise the way you're doing it. How very odd.
katrielalex
hmm.. Could it be a error in the wiki?
pyguy12
No, I've checked and that is the correct sign. Are you sure it's actually rotating the 'wrong' way? You're not flipping/reflecting/hacking the output before displaying it?
katrielalex
+1  A: 

About translating it, rotating and translating again, you effectively have to do this. However if you calculate the transformation matrix for each step once and multiply them together to get one transformation matrix that includes the two transforms ond the rotation then you only need to multiply each vertex by one matrix. To include translation in matrix transforms you need to use "homogenous coordinates" - see further down the wiki article. Basically you use coordinates (x,y,1) instead of (x,y) and then use a 3x3 matrix. The extra numbers allow for transformation.

neil
+1 for homogeneous coordinates.
katrielalex
Thanks I will look into this.
pyguy12
This looks like this is a harder concept to digest :-). Been looking at http://www.teamten.com/lawrence/graphics/homogeneous/ for a while to get a grasp of this.
pyguy12
It is, especially if you try to get into the maths (the projective plane is beautiful and elegant, but horrendous if you try to visualise it). But fortunately, it *just works*!
katrielalex