Amiga.org

Coffee House => Coffee House Boards => CH / Science and Technology => Topic started by: motorollin on September 11, 2007, 08:25:39 PM

Title: C++ Trigonometry
Post by: motorollin on September 11, 2007, 08:25:39 PM
I'm trying to calculate an angle given the distances between two objects:

int distx = playerx-x;
int disty = playery-y;
int angle = atan( disty/distx) * (180/3.14159265);
fprintf( stdout, "%d \n",angle );

Where x=100 y=100, playerx=0 and playery=0, the angle returned is 45 which sounds correct. But if the object is put on the opposite side (x=-100, y=-100) then the angle is still 45. What an I doing wrong?

--
moto
Title: Re: C++ Trigonometry
Post by: motorollin on September 11, 2007, 08:29:19 PM
Also, moving the object to x=100,y=-100 makes the angle -45 degrees.

--
moto
Title: Re: C++ Trigonometry
Post by: Karlos on September 11, 2007, 08:40:41 PM
I think atan() has sign ambiguity problems with respect to your needs - it will always return the smallest angle subtended.

Try using atan2(y, x) instead.

-edit-

Also, don't forget: the sort of trig you are doing assumes cartesian coordinate axes. Pixel coordinates, of course, are the opposite way around with respect to the y axis.
Title: Re: C++ Trigonometry
Post by: motorollin on September 11, 2007, 08:42:58 PM
Thanks. In atan2(), are y and x the distances between the two points?

--
moto
Title: Re: C++ Trigonometry
Post by: motorollin on September 11, 2007, 08:45:37 PM
Yeah! atan2( disty, distx ) works :-D

Cheers Karlos

--
moto
Title: Re: C++ Trigonometry
Post by: Karlos on September 11, 2007, 08:48:01 PM
Yes.
Title: Re: C++ Trigonometry
Post by: Boot_WB on September 11, 2007, 11:03:28 PM
Quote

motorollin wrote:
I'm trying to calculate an angle given the distances between two objects:

int distx = playerx-x;
int disty = playery-y;
int angle = atan( disty/distx) * (180/3.14159265);
fprintf( stdout, "%d \n",angle );

Where x=100 y=100, playerx=0 and playery=0, the angle returned is 45 which sounds correct. But if the object is put on the opposite side (x=-100, y=-100) then the angle is still 45. What an I doing wrong?

--
moto


This is due to the nature of tan, and the way it is generally handled.

Given that the convention of angles existing thus:
(http://i2.tinypic.com/5xzdysn.jpg)















And the graph of tan looks (roughly) like this:
(http://i5.tinypic.com/4uwair7.gif)






















The Tan of (for example) 45 degrees (tan(45)) = 1, but the tan of 180+45 degrees (Tan(225)) also = 1.
Consequently when doing an inverse tan of this value (arctan(1)) there are two possible angles which could be returned (45 and 225).
Conventionally, calculators will return values between -90 and 90 degrees for sin and tan, and 90 - 180 degrees for cos (both sin and cos have similar, albeit different, properties with regard to the full 360 degree range).
One way out would be to set up a series of "if" statements to compensate for this, adding the appropriate multiples of 90 degrees depending on which quadrant of the circle your object is located in.

I see you've already solved it with Karlos's help, but thought you might like the insight into why this occurs. :-)
Title: Re: C++ Trigonometry
Post by: Boot_WB on September 11, 2007, 11:06:53 PM
Just noticed - a topic on C++ Trigonometry, and only 14 people have viewed it!
Call yourselves geeks? :lol:
Title: Re: C++ Trigonometry
Post by: motorollin on September 12, 2007, 08:00:47 AM
Thanks Boot_WB, very interesting. I did consider calculating which quadrant the object is in and adding a multiple of 90 degrees to compensate, but fortunately atan2(x,y) does the hard work for me.

Now, here's another problem. The following code is supposed to move an object towards the player:

    //Find the angle required to move towards the player
    int distx = playerx-x;
    int disty = playery-y;
    int angle = atan2( disty, distx) * 57.29578;

    //Set the velocities
    dx = seekerspeed * cos(angle);
    dy = seekerspeed * sin(angle);

    //Move
    x+=dx;
    y+=dy;


As you can see in this Quicktime video (http://www.mashley.net/seeker_move.mov), the effects aren't quite as desired. The enemy stops when it gets close to the player instead of attempting to collide with it, and if the player moves the enemy actually seems to move away from it!

--
moto
Title: Re: C++ Trigonometry
Post by: motorollin on September 12, 2007, 08:20:31 AM
Someone on another forum has solved this for me. The  "* 57.29578" shouldn't be there because cos() and sin() take radians, not degrees.

--
moto
Title: Re: C++ Trigonometry
Post by: Cymric on September 12, 2007, 08:29:36 AM
I suggest you remove the '* 57.29578' bit for this part of the program. cos() and sin() require an argument in radians, not degrees. Oh, and please, do write 'double angle = ...'; angles are not integers :-). Then, to avoid those nasty type promotion rules of C++ noone---not even Karlos---can properly remember, I suggest you code as follows:

---------------------

double dx, dy, seekerspeed = SOME_DOUBLE_VAL;
double distx = playerx - x;
double disty = playery - y;
double angle = atan2(disty, distx);

dx = seekerspeed * cos(angle);
dy = seekerspeed * sin(angle);

x = x + (int)dx;
y = y + (int)dy;

----------------------

This has the advantage of keeping everything double which needs to be double right up to the point where it needs to become an integer. It's probably too C-like, but I like it this way anwyay. And I would code it in such a way that things were single rather than double, saves a lot of computational effort. Unfortunately, things are a bit iffy with the linker libraries, which sometime promote everything to double just the same.

You should also expect quite a lot of jittery behaviour when the seeker is near the target, as the number of possible directions the seeker can take is limited to a maximum of 8. A better and smoother result is obtained if you do all calculations of seeker and target movement in floating point (either free or fixed), and then translate those to screen coordinates only when you need to draw them. A little fudging is probably required to avoid exclamations of 'I WAS MILES AWAY FROM THAT BLASTED THING, ()&*)&U_&U_#Q!'
Title: Re: C++ Trigonometry
Post by: motorollin on September 12, 2007, 12:09:24 PM
Actually I am now using this:

int distx = playerx-x;
int disty = playery-y;
int dist=sqrt(distx*distx+disty*disty);

dx=speed*distx/dist;
dy=speed*disty/dist;



This is much faster, since floating point maths and trig is very slow on the GP2X (no FPU so all float operations are done in software). Also this code seems to result in smoother and more accurate motion.

--
moto
Title: Re: C++ Trigonometry
Post by: Karlos on September 12, 2007, 01:29:59 PM
If you have no FPU, the sqrt() call is still a killer though. If you are using integer only, it might be worth looking to see if there are any approximation methods for "nearest integer root" that don't rely on sqrt() calls at all.

Also, as I've suggested before, if you want a balance of precision and performance, consider using fixed point maths. You can easily write a concrete FixedPoint class that wraps a single 32-bit integer and have it support, say 16.16 fixed point format. You could provide all the standard arithmetic operator methods for it.