Allegro.cc - Online Community

Allegro.cc Forums » Game Design & Concepts » Failing to rotate a point around a central point

This thread is locked; no one can reply to it. rss feed Print
Failing to rotate a point around a central point
David Sopala
Member #5,056
September 2004

As per my last post I got that problem solved, and I also read that website down near the bottom of the post as per Neil: http://www.helixsoft.nl/articles/circle/sincos.htm

This gave me the though to add hit zones or collision circles that identify the hit zones on an object. Seemed simple enough I use a defining point of x,y and rotate that around a central point at the same rate as the image rotates. This is the code I wrote to accomplish my goal, I also have a function to draw green circles to show where the C_Circs are placed on the spider. Currently they aren't drawing correctly I get white circles, but I can see them(guessing it is a mode setting in my drawing routine or the alpha channel). After I use a key to rotate the spider the circles just disappear I assume this is because of some misinterpretation of the atan2 function or a misconversion to a fixed. Going to keep playing with it and see if I can find my error any help would be appreciated.

EDIT: Found my circles decided to do a blit to force it to draw without alpha or masking(and learned even more from that!) For some reason the circles are stuck in the top left corner only in a couple rotation positions though.

EDIT 2: Attached a copy of what is going on to help explain things.

#SelectExpand
1void C_Circ::Rotate_Around_Point(int X,int Y,int Degrees) 2{ 3 //all we need to do is update the center point (x,y) of the C_Circ 4 //find my current angle 5 //by normalizing the coordinates to the central point of rotation 6 fixed cur_angle = atan2(itofix(x-X),itofix(y-Y)); 7 //add Degrees to current angle 8 cur_angle += itofix(Degrees); 9 cur_angle &= 0xFFFFFF; 10 //calculate new x,y from new angle 11 12 int length = sqrt((x-X)*(x-X) + (y-Y)*(y-Y)); 13 x = length * fcos(cur_angle); 14 y = length * fsin(cur_angle); 15}

Johan Halmén
Member #1,550
September 2001

Wrong approach. You should not find any current angle, add rotation and use the new angle to find the new point. Theoretically it works, but there's a much shorter way. In your code you seem to forget to add back the center point.

Anyway, here's what you should do:
First, simplify your rotation. Think of it as rotating around origin. Divide (x, y) into (x, 0) plus (0, y). How do you rotate (x, 0)? That's (x * cos(a), x * sin(a)), right? How do you rotate (0, y)? That's (-y * sin(a), y * cos(a)), right. Add these and you get:
x1 = x * cos(a) - y * sin(a)
y1 = x * sin(a) + y * cos(a)

If I made an error, the idea should still be clear. Just check it all :)

Ok, you're rotating around X, Y. Do what you did in your code (but don't forget the last part):
x = x - X;
y = y - Y;
Do my rotation around origin
x = x + X;
y = y + Y;

Or uglify everything by not rotating around origin.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Years of thorough research have revealed that the red "x" that closes a window, really isn't red, but white on red background.

Years of thorough research have revealed that what people find beautiful about the Mandelbrot set is not the set itself, but all the rest.

David Sopala
Member #5,056
September 2004

After tinkering with the code I can explain my thought process as to why exactly I was doing things the way I was trying to accomplish them.

You should not find any current angle, add rotation and use the new angle to find the new point.

The reasoning for that is one of my C_Circ classes may not start at 0 degrees of rotation. These are hit zones on a mob ex(Head shots, body shots, or other vital area shots). This class can potentially start anywhere that is possible in the sprite.

Problem I have found: Length is returning inconsistant values for some reason making the points jump all over the place.

Loading information into a C_Circ class:

...

magic_number = (int)ceil(sqrt(Master->w*Master->w + Master->h*Master->h));

...

loadstream>>cx>>cy>>cr>>multiplier;   //ignoring multiplier for now.

        Hit_Zones[lcv] = new C_Circ(cx+(magic_number-Master->w)/2,cy+(magic_number-Master->h)/2,cr);

Class constructor:

     C_Circ::C_Circ(int X,int Y,int R)
{
    x = X;
    y = Y;
    rad = R;
}

Here we have the monster rotation code I intend to switch over to vectors for my container, but the netbook I compile on when I am at work on break or whatever doesn't seen to have vector.h and I haven't remembered to copy it over.

#SelectExpand
1void Monster::Rotate_Left() 2{ 3 if(abs(dr - r) > 4) 4 { 5 r -= 5; 6 for(int lcv = 0;lcv < num_col;lcv++) 7 { 8 Hit_Zones[lcv]->Rotate_Around_Point(ceil(MAGIC->w/2),ceil(MAGIC->h/2),r); 9 } 10 } 11 else 12 { 13 r -= abs(dr - r); 14 for(int lcv = 0;lcv < num_col;lcv++) 15 { 16 Hit_Zones[lcv]->Rotate_Around_Point(ceil(MAGIC->w/2),ceil(MAGIC->h/2),r); 17 } 18 } 19}

For the function call to rotate
NOTE: I have set my length to a static 16 to do error checking(and I can see one hit zone work properly). I assume I could just use sin(Degrees) and drop all the conversions, but for now getting functionality and then later making it optimized will work.

void C_Circ::Rotate_Around_Point(int X,int Y,int Degrees)
{
    int Length = sqrt(((X-x)*(X-x))+((Y-y)*(Y-y)));

    x = X + 16 * fixtoi(fcos(itofix(Degrees)));
    y = Y + 16 * fixtoi(fsin(itofix(Degrees)));
}

EDIT:
Now after all that you must be thinking "You should use the code Johan posted" his code becomes unstable as well.

void C_Circ::Rotate_Around_Point(int X,int Y,int Degrees)
{
    //int Length = sqrt((abs(X-x)*abs(X-x))+(abs(Y-y)*abs(Y-y)));

    x = X + (X-x) * fixtoi(fcos(itofix(Degrees))) - (Y-y) * fixtoi(fsin(itofix(Degrees)));
    y = Y + (X-x) * fixtoi(fsin(itofix(Degrees))) + (Y-y) * fixtoi(fcos(itofix(Degrees)));


    //x = X + Length * fixtoi(fcos(itofix(Degrees)));
    //y = Y + Length * fixtoi(fsin(itofix(Degrees)));
}

EDIT #3:So I started outputting everything into a txt file so I could see what was going on.... What I learned:

fixtoi(fsin(itofix(r))) or similar functions will give no precision guess I should have noticed int and not float or something similar... -1 0 1 are the only states given by this method might work for an 8 direction numpad type game, but not for anything with true angles.

Rounding errors are going to be a nightmare as I watch the circles slowly move to the origin point as the sprite rotates now...

huh<<"C: "<<lcv<<endl;
           huh<<"WIDTH OF BMP/2: "<<ceil(MAGIC->w/2)<<endl;
           huh<<"HEIGHT OF BMP/2: "<<ceil(MAGIC->h/2)<<endl;
           huh<<"HZ_X: "<<Hit_Zones[lcv]->Get_X()<<endl;
           huh<<"HZ_Y: "<<Hit_Zones[lcv]->Get_Y()<<endl;
           huh<<"ANGLE: "<<r<<endl;
           huh<<"SIN ANGLE: "<<fsin(itofix(r))<<endl;
           huh<<"COS ANGLE: "<<fcos(itofix(r))<<endl;
           huh<<"d65536 SIN ANGLE: "<<(float)fsin(itofix(r))/65536<<endl;
           huh<<"d65536 COS ANGLE: "<<(float)fcos(itofix(r))/65536<<endl;
           huh<<"F_SIN ANGLE: "<<fixtoi(fsin(itofix(r)))<<endl;
           huh<<"F_COS ANGLE: "<<fixtoi(fcos(itofix(r)))<<endl;
           huh<<"LENGTH: "<<sqrt((abs(ceil(MAGIC->w/2)-Hit_Zones[lcv]->Get_X()) * abs(ceil(MAGIC->w/2)-Hit_Zones[lcv]->Get_X())) + (abs(ceil(MAGIC->h/2)-Hit_Zones[lcv]->Get_Y()) * abs(ceil(MAGIC->h/2)-Hit_Zones[lcv]->Get_Y())))<<endl<<endl;

#SelectExpand
1C: 0 2WIDTH OF BMP/2: 45 3HEIGHT OF BMP/2: 45 4HZ_X: 30 5HZ_Y: 45 6ANGLE: -5 7SIN ANGLE: -8022 8COS ANGLE: 65043 9d65536 SIN ANGLE: -0.122406 10d65536 COS ANGLE: 0.992477 11F_SIN ANGLE: 0 12F_COS ANGLE: 1 13LENGTH: 15 14 15C: 1 16WIDTH OF BMP/2: 45 17HEIGHT OF BMP/2: 45 18HZ_X: 41 19HZ_Y: 45 20ANGLE: -5 21SIN ANGLE: -8022 22COS ANGLE: 65043 23d65536 SIN ANGLE: -0.122406 24d65536 COS ANGLE: 0.992477 25F_SIN ANGLE: 0 26F_COS ANGLE: 1 27LENGTH: 4 28 29C: 2 30WIDTH OF BMP/2: 45 31HEIGHT OF BMP/2: 45 32HZ_X: 61 33HZ_Y: 44 34ANGLE: -5 35SIN ANGLE: -8022 36COS ANGLE: 65043 37d65536 SIN ANGLE: -0.122406 38d65536 COS ANGLE: 0.992477 39F_SIN ANGLE: 0 40F_COS ANGLE: 1 41LENGTH: 16.0312 42 43C: 0 44WIDTH OF BMP/2: 45 45HEIGHT OF BMP/2: 45 46HZ_X: 0 47HZ_Y: 0 48ANGLE: 246 49SIN ANGLE: -15924 50COS ANGLE: 63572 51d65536 SIN ANGLE: -0.242981 52d65536 COS ANGLE: 0.970032 53F_SIN ANGLE: 0 54F_COS ANGLE: 1 55LENGTH: 63.6396 56 57C: 1 58WIDTH OF BMP/2: 45 59HEIGHT OF BMP/2: 45 60HZ_X: 0 61HZ_Y: 0 62ANGLE: 246 63SIN ANGLE: -15924 64COS ANGLE: 63572 65d65536 SIN ANGLE: -0.242981 66d65536 COS ANGLE: 0.970032 67F_SIN ANGLE: 0 68F_COS ANGLE: 1 69LENGTH: 63.6396 70 71C: 2 72WIDTH OF BMP/2: 45 73HEIGHT OF BMP/2: 45 74HZ_X: 0 75HZ_Y: 0 76ANGLE: 246 77SIN ANGLE: -15924 78COS ANGLE: 63572 79d65536 SIN ANGLE: -0.242981 80d65536 COS ANGLE: 0.970032 81F_SIN ANGLE: 0 82F_COS ANGLE: 1 83LENGTH: 63.6396

FINAL EDIT:

Finally got the issues fixed. Instead of recalculating the length every time(which was introducing rounding issues). Getting rid of the recalculating length fixed most of the issues, just had to do a little bit of sign preservation.

void C_Circ::Rotate_Around_Point(int X,int Y,int Degrees)
{
    if(RX != X || RY != Y)  //Not the same rotation point!
    {
        RX = X;
        RY = Y;
        RL = sqrt((abs(X-x)*abs(X-x))+(abs(Y-y)*abs(Y-y)));
        if(X - x > 0)RL *= -1;
        if(Y - y < 0)RL *= -1;
    }
    x = X + (int)(RL * (double)fcos(itofix(Degrees)) / 65536);
    y = Y + (int)(RL * (double)fsin(itofix(Degrees)) / 65536);
}

Johan Halmén
Member #1,550
September 2001

The reasoning for that is one of my C_Circ classes may not start at 0 degrees of rotation.

Irrelevant. You have a point (x, y) and you want to rotate it around point (X, Y) by an angle Degrees, right?
I'd still do it like this:

void C_Circ::Rotate_Around_Point(int X,int Y,int Degrees)
{
    float x1, y1;
    x -= X;
    y -= Y;
    x1 = x * cos(Degrees) - y * sin(Degrees) + X;
    y1 = x * sin(Degrees) + y * cos(Degrees) + Y;
    x = x1;
    y = y1;
}

And if you want speed, you should calculate cos and sin only once:

void C_Circ::Rotate_Around_Point(int X,int Y,int Degrees)
{
    double x1, y1, cos_degrees, sin_degrees;
    x -= X;
    y -= Y;
    x1 = x * (cos_degrees = cos(Degrees)) - y * (sin_degrees = sin(Degrees)) + X;
    y1 = x * sin_degrees + y * cos_degrees + Y;
    x = x1;
    y = y1;
}

As for rounding errors, use doubles, not ints. X and Y may be ints, if they really are just screen coordinates. But x and y, if they define your object position which change through rotation and whatnot, should be doubles.
In my code, the only expensive calculations are one sin and one cos. Where you calculate the distance with Pythagoras, in my code it's reduced to x -= X and y -= Y. You don't need the fixed functions here. People say double is as fast as fixed nowadays. fixed maybe is needed only when you do rotate_sprite() in Allegro 4.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Years of thorough research have revealed that the red "x" that closes a window, really isn't red, but white on red background.

Years of thorough research have revealed that what people find beautiful about the Mandelbrot set is not the set itself, but all the rest.

David Sopala
Member #5,056
September 2004

I wasn't having as much of a problem with the sin / cos rounding, but the sqrt rounding was coming out of no where. I think this solution is faster than yours even assuming I rotate around one point at a time.

FINAL EDIT:
Finally got the issues fixed. Instead of recalculating the length every time(which was introducing rounding issues). Getting rid of the recalculating length fixed most of the issues, just had to do a little bit of sign preservation.

void C_Circ::Rotate_Around_Point(int X,int Y,int Degrees)
{
    if(RX != X || RY != Y)  //Not the same rotation point!
    {
        RX = X;
        RY = Y;
        RL = sqrt((abs(X-x)*abs(X-x))+(abs(Y-y)*abs(Y-y)));
        if(X - x > 0)RL *= -1;
        if(Y - y < 0)RL *= -1;
    }
    x = X + (int)(RL * (double)fcos(itofix(Degrees)) / 65536);
    y = Y + (int)(RL * (double)fsin(itofix(Degrees)) / 65536);
}

SiegeLord
Member #7,827
October 2006
avatar

I think this solution is faster than yours even assuming I rotate around one point at a time.

His solution has one call to sin and cos, while yours has one call to sin and cos and one call to sqrt. How can it possibly be faster?

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

verthex
Member #11,340
September 2009
avatar

SiegeLord said:

How can it possibly be faster?

The inverse square root function using that special number with Newtons method might work.

David Sopala
Member #5,056
September 2004

Mine calls that once assuming one point of rotation.

If I recall my asm a compare is a 4 tick command which TBH would be faster than the extra casting calls and the like. His code is also flawed in the fact that Degrees in 0 - 256 so it is missing the conversion there not a big thing. My sqrt only occurs in the first itteration, after that it does not get called again untill we change our rotation point(which never changes as it is the center of the sprite and not a position in the world.) While each sprite has hit zones, they each have a master zone which is used in world calculations. Either code works I think, but origonally his code did not work, and neither did mine I had fixed the issue only to find a few days later that there are replys which I am grateful for as it does help to see from a different point of view. I would rather post my code and get it working my way if it is possible then be shown another possibly more efficent way. I had reffered to his origonal post with 2xsin and 2xcos one call to sqrt(since that part of the code never changes the rotation point) and only one call to cos and sin each seemed alot better.

To show what has been accomplished I am attaching an exe. Still need to tweak the movement a little since the spider seems to have issues getting to the correct point when he is walking(a bit of walking sideways apparently fixes that, but looks horrible)

Schyfis
Member #9,752
May 2008
avatar

I just use this:

public void rotateAboutPoint(Location point, float r) {
      float n = dist(point.getX(), point.getY(), getX(), getY());
      float a = point.getX() - getX(), b = point.getY() - getY();
      r -= (float)(Math.Atan2(b, a));
      moveTo(point.getX() - (float)(n * Math.Cos(r)), point.getY() + (float)(n * Math.Sin(r)));
}

It's probably slow, but it works just fine and is easy to understand.

________________________________________________________________________________________________________
[freedwill.us]
[unTied Games]

David Sopala
Member #5,056
September 2004

Isn't atan2 undefined for angle = 0? I know that was in the reading I did...

Johan Halmén
Member #1,550
September 2001

atan2(a, b) is probably undefined when a and b both equal 0.

Schyfis said:

and is easy to understand

Well, go through this:
{"name":"605647","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/0\/7\/0712d4a6e70c7308d4a4830d6f07d737.png","w":400,"h":300,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/0\/7\/0712d4a6e70c7308d4a4830d6f07d737"}605647
You have x1 y1 and you want to rotate it by the angle of a to reach x2 y2.
Well, x1 y1 is practically x1 0 plus 0 y1, right? When you rotate x1 y1 and get x2 y2, it's same as first rotating x1 0 to get to r1, then rotating 0 y1 to get r2...
{"name":"605648","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/e\/7\/e7e1a75dd99cfe4764015b8f487efbc6.png","w":400,"h":300,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/e\/7\/e7e1a75dd99cfe4764015b8f487efbc6"}605648
Some maths here. xr1 = x1 * cos(a), yr1 = x1 * sin(a)
xr2 = -y1 * sin(a), yr2 = y1 * cos(a)

Now you can add r1 and r2...
{"name":"605649","src":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/d\/2\/d27450d956545c051f0399eae599839f.png","w":400,"h":300,"tn":"\/\/djungxnpq2nug.cloudfront.net\/image\/cache\/d\/2\/d27450d956545c051f0399eae599839f"}605649
...and you get:
x2 = x1 * cos(a) - y1 * sin(a)
y2 = x1 * sin(a) + y1 * cos(a)

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Years of thorough research have revealed that the red "x" that closes a window, really isn't red, but white on red background.

Years of thorough research have revealed that what people find beautiful about the Mandelbrot set is not the set itself, but all the rest.

Go to: