Skip to main content
GameDev.net gamedev.net
🔒 Locked

2D Point Rotation

Started by CrewNick Mar 28, 2007 at 11:55 AM 12 replies 1.9k views
Original Post
CrewNick
CrewNick
Ok, before someone yells at me for asking a simple question, I know how to use sign/cos (in theory) for point rotation. :-p Here's my problem: I've got a java game I'm working on whose core (as many 2d game's cores are) based on 2d points. I've got my "MyPoint" object which has the following method: //x and y are both doubles public void rotate(MyPoint center, double phi) { double angle = Math.atan2(x-center.getX(),y-center.getY()); double dist = Math.sqrt((x-center.getX()) * (x-center.getX()) + (y-center.getY()) * (y-center.getY())); x = Math.sin(angle + phi) * dist + center.getX(); y = Math.cos(angle + phi) * dist + center.getY(); } It's supposed to rotate this point around the point center by phi degrees. My test to see if this works is to rotate a point around a fixed point by 1 degree 360 times (as follows): public static void main(String[] args) { MyPoint p = new MyPoint (100,150); p.print(); System.out.println(); for(int i = 0; i < 360; i++) { p.rotate(new MyPoint(100,100),Math.toRadians(1)); p.print(); System.out.println(); } } however, the output ends up like this: 100,150 . . . 99, 150 The x value of 99 is throwing off everything in my game. Does anyone know why this is happening?
Daerax
Daerax
In the plane R two - the 2D plane, rotation of some point (x,y) through some angle 'a' is defined as:

(x,y) |-> ( x*cos(a) - y *sin(a) , x * sin(a) + y * cos(a) )

x = t + rcos(a)
y = u + rsin(a)

is the parametric definition of a circle.
rockdeity
rockdeity
Ok, sanity check here. How do you know it is wrong... just based on the output? Are you outputting int values instead of float/double values?

Also, this doesn't actually matter I think in only this code snippet but it might be a problem if you are inconsistent with this in other places in your code, but your interpretation of x and y is counter to convention here. You are using x as the vertical component and y as the horizontal. If you use it the other way outside of this code, in passing in the "center" param, for example, it could cause undefined behavior.
Raghar
Raghar
Quote:
Original post by CrewNick
p.rotate(new MyPoint(100,100),Math.toRadians(1));
however, the output ends up like this:

100,150
.
99, 150

The x value of 99 is throwing off everything in my game. Does anyone know why this is happening?


The rotation is defined (if center of rotation is in 0,0) as

a = a*cos(u) - b* sin(u);
b = b*cos(u) + a*sin(u);

or

a = a*cos(u) + b* sin(u);
b = b*cos(u) - a*sin(u);

these two equations were on purpose created to avoid that nasty sqrt(...)

BTW you have a little problem, as you are not protecting yourself from rounding error well. Try it without atan2 method.

Often doing:
A' = rotate(v) A
A' ' = rotate(v) A'
is inferior to
A' = rotate(v) A
A' ' = rotate(v') A

edit (slight oversight, and this board dislike two ' in a row)
Daerax
Daerax
He is not rotating the point so much as he is plotting the point of some circle whose radius is the distance from some (x,y) to the point passed (the centre) at the angle "angle + phi".

Note that the rotation formula i gave above rotates through the origin so to rotate an object about its centre you define x,y in terms of distances from the centre of the object.
CrewNick
CrewNick
Thanks for the responses! This has been driving me crazy.

I'll try out the new formula's WITHOUT using the atan2 or sqrt method.

And yes, rounding errors seem to where my problem lies. I haven't had a chance to check out the above formula's in depth yet, but I will when I get the chance. I'm not quite about how to solve the rounding errors at the moment through I'll take a closer look at this to see if it can help:

Often doing:
A' = rotate(v) A
A' ' = rotate(v) A'
is inferior to
A' = rotate(v) A
A' ' = rotate(v') A
Daerax
Daerax
Numerical methods are certainly not my specialty but i believe that due to precision reasons, when you rotate iteratively - that is each rotation is done in terms of the prior coordinates - you get a build up of errors till you start to get wrong results as the numbers start to diverge.

Since the composition of a set of rotation is equivalent to performing one rotation that is the sum of the angles of the each of the rotations taken individually it does to rotate from some initial set of coordinates by the sum of the given angles (which you keep track of) each time.
CrewNick
CrewNick
That sounds like the conclusion I've been coming to. With a prior game, where polygons rotated around their own center's, I overcame this by defining each point by it's distance/rotation from the polygons center. I would keep the original rotation value constant and then just add/subtract from that in order to rotate the object (ok, a little more complicated than that, but you get the idea).

In this case, I can't really do that because of the nature of the game. There are points, lines and polygons which can be combined in a few ways to make a "part". Each part will have sub-parts until you get to an atomic part (Line, point or polygon). When one part rotates around a point, all the subparts rotate around the same point too.

I can't think of a way to do this other than to define a method like:
public void rotate(MyPoint center, double phi)

If anyone has any suggestions on another way of doing this or a way to get rid of the precision errors, that would be greatly appreciated.
nb
nb
p.rotate(new MyPoint(100,100),Math.toRadians(1)); <-- should that Math.toRadians be an 'i' not a '1'? I'll assume you didn't copy/paste else surely that would ruin all of the rotations you are trying to observe.
nagromo
nagromo
Quote:
Original post by CrewNick
That sounds like the conclusion I've been coming to. With a prior game, where polygons rotated around their own center's, I overcame this by defining each point by it's distance/rotation from the polygons center. I would keep the original rotation value constant and then just add/subtract from that in order to rotate the object (ok, a little more complicated than that, but you get the idea).

In this case, I can't really do that because of the nature of the game. There are points, lines and polygons which can be combined in a few ways to make a "part". Each part will have sub-parts until you get to an atomic part (Line, point or polygon). When one part rotates around a point, all the subparts rotate around the same point too.

I can't think of a way to do this other than to define a method like:
public void rotate(MyPoint center, double phi)

If anyone has any suggestions on another way of doing this or a way to get rid of the precision errors, that would be greatly appreciated.


Keep a variable for how far each point is rotated around the center and what its position is relative to the center at angle 0. That way, when you rotate the object, you add to its angle variable, and to find its actual position, you use the same relative position that never changes, avoiding the precision problems.

actual_pos=center_pos + relative_pos.rotate(angle);

where rotate(angle) rotates around the origin.

Find relative_pos only when the part it's connected to changes, i.e. there's a new center or it's removed from the part. That way, the rounding errors add up very, very slowly.
CrewNick
CrewNick
I think that could work. I could even "reset" the precision error by every time the rotation passes 360 degrees, reset it to 0 degrees.

I'll try something like the following and let you guys know how it goes. I have a sneaking suspicion that I'll still get in trouble with relativeDist/Angle's percision, but it's worth a shot :-p

MyPoint:
double startDist, startAngle; // starting distance/angle from the origin
double relativeDist, relativeAngle; //rotation/movement amount
//aka define through polar coordinates rather than Cartesian
nagromo
nagromo
Quote:
Original post by CrewNick
I think that could work. I could even "reset" the precision error by every time the rotation passes 360 degrees, reset it to 0 degrees.

I'll try something like the following and let you guys know how it goes. I have a sneaking suspicion that I'll still get in trouble with relativeDist/Angle's percision, but it's worth a shot :-p

MyPoint:
double startDist, startAngle; // starting distance/angle from the origin
double relativeDist, relativeAngle; //rotation/movement amount
//aka define through polar coordinates rather than Cartesian


No, you won't have a problem with precision! You just use the parent's angle to find the child's absolute position. The variable you're finding isn't used in future calculations, so errors don't add up.

Here's a way you could do it, storing everything as MyPoint.

MyPoint:
MyPoint* parent
double relX,relY;
double angle;
double absX,absY;
List children;

when you rotate an object, you are rotating all of its children relative to it.

FindAbsolutePos():
absX=parent.absX+relX*cos(parent.angle)+relY*sin(parent.angle);
absY=parent.absY+relY*cos(parent.angle)-relX*sin(parent.angle);

foreach(MyPoint child in children)
child.FindAbsolutePos();

Just call that every time relX, relY, or angle changes. Make a MyPoint representing the origin that never changes, and you can represent everything this way! Also, you won't have any problems with precision because absX and absY are recalculated every time!

Here's a square:

square = new MyPoint(parent=origin,relX=0,relY=0,angle=0);
square.children.add(new MyPoint(parent=square,relX=1,relY=1);
square.children.add(new MyPoint(parent=square,relX=1,relY=-1);
square.children.add(new MyPoint(parent=square,relX=-1,relY=1);
square.children.add(new MyPoint(parent=square,relX=-1,relY=-1);
square.FindAbsolutePos();

Now, rotate the square:

square.angle += 0.18;
square.FindAbsolutePos();

Move the square:

square.relX += 18.0;
square.relY += -10;
square.FindAbsolutePos();

and every time, all the children are kept up to date. You can even have the square act as one of the points of a star! Just say star.children.add(square) and set the relative position of the center of the square relative to the center of the star. It's a really simple way to make heirarchies of these 'parts'.

This is basically a 2D 'scene graph'.

[Edited by - nagromo on March 29, 2007 10:39:29 PM]
CrewNick
CrewNick
Everything looks really good there except for your move command. If you move the square that way, you'll have to re-calculate the relative angle again. Same for the rotate command, you'll have to recalculate relativeX and relativeY if you plan on using them in the rotate function :-p.
The move/rotate command may have to be done like this:
(algorithm only cause it's late here :-p)


//recalculate angle after moving
Move(dx, dy)
relX += dx;
relY += dy;
//parent acts as this points origin
angle = atan2(relX, relY);

//recalculate relX, relY after rotating for use in move command or future rotate commands
Rotate(double radians)
angle += radians;
tempX=relX*cos(angle)+relY*sin(angle);
tempY=relY*cos(angle)-relX*sin(angle);
relX = tempX;
relY = tempY;

P.S. would it be better to store the angle and distance and get rid of relativeX and relativeY all together? Or is there some math/precision property which makes

tempX=relX*cos(angle)+relY*sin(angle);
tempY=relY*cos(angle)-relX*sin(angle);
relX = tempX;
relY = tempY;

superior to

relX = distance * cos(angle)
relY = distance * sin(angle)

?
Raghar
Raghar
In game development these things are often done by aplying an angular velocity, and a linear velocity to allow confortable application of impulses.

Obviously if you'd calculate an sin/cos for every point, the simulation would go into the crawl. With a = a*cos(angle) - b*sin(angle), the sin/cos of angle is calculated only once for one rotation. thus you'd have at = a; bt = b; a = at*const - bt*const2

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.