Trigonometric find angle

edited July 2013 in Development
Hi guys,
anyone knows a formula to calculate an angle between two coordinates, using basic?s function ATN , recent programming tools have ATAN2(dy,dx), but im having trouble using atn.
thanx in advance.
Post edited by Valz on

Comments

  • edited July 2013
    Write your own version of atan2.

    ATN returns values in range -pi to pi I expect EDIT: I mean -pi/2 to pi/2, you need to check which quadrant y and x are in and adjust the angle accordingly.

    x > 0: just return ATN(y/x)

    x < 0: if(y > 0) return ATN(y/x) + PI
    x < 0: if(y < 0) return ATN(y/x) - PI

    x = 0 return PI/2 if y > 0, -PI/2 otherwise.

    Here is a link to a C implementation:

    http://www.raspberryginger.com/jbailey/minix/html/atan2_8c-source.html

    EDIT: Most of the time you can avoid trig by using vectors though...
  • edited July 2013
    You'd be much better off calculating the length of the hypotenuse then using ACS or ASN instead. ATN just won't work for angles around 90 or 270 degrees.
    Still supporting Multi-Platform Arcade Game Designer, currently working on AGD 5. I am NOT on Twitter.
    Egghead Website
    Arcade Game Designer
    My itch.io page
  • edited July 2013
    I tried that formula and still can?t get a result, it should be between -180 and 180 right?
    here?s what i have done in zxbasic compiler:




    dim dx,dy as integer rem range -32768..32767
    dim value as fixed rem range -32767.9999847 to 32767.9999847

    cls
    print getangle (100,100,100,50) rem 89,expected 0
    print getangle (100,100,150,100) rem -88,expected 90
    print getangle (100,100,100,150) rem 89,expected 180
    print getangle (100,100,50,100) rem -89,expected -90

    end


    function getangle (x1 as ubyte,y1 as ubyte,x2 as ubyte,y2 as ubyte) as fixed
    dx=x2-x1
    dy=y2-y1

    if dx>0 then value= atn(dy-dx):end if
    if dx<0 and dy>0 then value=atn(dy/dx)+pi:end if
    if dx<0 and dy<0 then value=atn(dy/dx)-pi:end if
    if dx=0 then
    if dy>0 then value=pi/2:end if
    if dy<0 then value=-pi/2:end if
    end if
    return int((value/pi)*180) rem to convert rads to degres right?
    end function

    can you find out what am i doing wrong? :)
  • edited July 2013
    if dx>0 then value= atn(dy-dx):end if

    you mean

    if dx>0 then value= atn(dy/dx):end if

    This case: getangle(100, 100, 100, 50) should return -90...

    x1 = 100
    y1 = 100
    x2 = 100
    y2 = 50

    dx = x2 - x1 = 0
    dy = y2 - y1 = 50 - 100 = -50

    if dx=0 then
    if dy>0 then value=pi/2:end if
    if dy<0 then value=-pi/2:end if
    end if

    so return int((value/pi)*180) should be

    ((-pi/2)/pi)*180 = -1/2 * 180 = -90

    Try printing out intermediate values are you sure it isn't truncating or doing integer division somewhere? why are you using ubyte as arguments?

    Remember the angle is measured from the X-axis (not the y-axis) and +ve angles are anticlockwise
  • edited July 2013
    oh, that was not supposed to be a subtraction there, corrected it :)
    but now i don?t have negative numbers, only zero:

    print getangle (100,100,80,120) rem 0
    print getangle (100,100,120,50) rem 84
    print getangle (100,100,110,50) rem 87
    print getangle (100,100,150,100) rem 0
    print getangle (100,100,100,150) rem 89
    print getangle (100,100,50,100) rem 0

    im using ubyte for the coordinates (0-255) value
    fixed for the decimals,after divisions
    and integer for the actual value of the angle,positive or negative

    maybe this doesn?t work, like johnathan said :(
  • edited July 2013
    is dy/dx doing integer division? because integer division probably discards any remainder... print out the value of dy/dx before you call ATN.
  • edited July 2013
    you were right about using ubyte there, changed all to fixed and added 90 to final result, and it seem to work :D

    dim dx,dy as fixed
    dim value as fixed
    cls

    print getangle (100,100,100,50) rem 0
    print getangle (100,100,120,50) rem 22
    print getangle (100,100,110,50) rem 12
    print getangle (100,100,150,100) rem 90
    print getangle (100,100,100,150) rem 180
    print getangle (100,100,50,100) rem -90

    function getangle (x1 as fixed,y1 as fixed,x2 as fixed,y2 as fixed) as fixed
    dx=x2-x1
    dy=y2-y1
    if dx>0 then value= atn(dy/dx):end if
    if dx<0 and dy>0 then value=atn(dy/dx)+pi:end if
    if dx<0 and dy<=0 then value=atn(dy/dx)-pi:end if
    if dx=0 then
    if dy>0 then value=pi/2:end if
    if dy<0 then value=-pi/2:end if
    end if
    return int((value/pi)*180)+90 rem to convert rads to degres right?
    end function



    it gives this type of result:
    .......0
    .-90...90
    ......180

    thanks a lot Paradigm :)
  • edited July 2013
    You shouldn't add 90 to the result...

    0 degrees is due EAST
    90 degrees is due NORTH
    +/-180 degrees is due WEST
    270 degrees/ -90 degrees is due SOUTH

    I do realise a lot of people seem to like measuring their angles clockwise from due north though, rather than anticlockwise from due east which is how we do it in maths ;)

    EDIT: Except bearings, but those are used for navigating ships ;) I don't use degrees either, I only ever use radians or some fixed point representation e.g. 256 "angs" in a full circle or something like that.
  • edited July 2013
    And I'll say this again, are you sure you need to use trig at all? Most things involving angles are often easier/faster to do with vectors and dot products instead... what exactly are you using the angle for? If it is picking the rotation of a sprite you can just work out the sector your facing vector is in, for example.
  • edited July 2013
    I?m using it mostly for calculating the AI of enemies,
    ie, if a enemy fires a bullet to the player like an homing missile or something,
    i find this a essencial function for this type of calculation, could use coordinate checking x<x2 etc, but this is much better. ;)
  • edited July 2013
    Use vectors for that... move the bullet towards the player by doing

    vToPlayer = vPlayerPos - vBulletPos

    vToPlayer = normalise(vToPlayer) // make length of vector 1 by dividing by the magnitude

    vBulletPos = vBulletPos + vToPlayer * bulletSpeed

    (things with a v in front of variable name are vectors)

    EDIT: You can replace the normalise step and instead do

    vBulletPos = vBulletPos + vToPlayer * bulletSpeed / vectorLength(vToPlayer)


    EDIT2: If you find yourself doing an ATAN2 followed by some SIN/COS stuff on the result, chances are you are better off doing it with vectors instead.
  • edited July 2013
    Yes, that?s exactlty what im doing, finding an angle and then add vertical and horizontal speed according to sin,cos.
    But if using vectors proves to be faster especially for real time calculations i will sure give it a try :)
  • edited July 2013
    Yeah, it will be faster and more accurate too ;)

    All you need to do is write routines to add and subtract vectors, multiply them by a scalar, dot product, find the length (which is the square root of the vector dot producted with itself), and normalisation (divide a vector by its length).

    A lot of the time you can use length squared (which is just the vector dot producted with itself) instead of the length, since if a < b and a, b are >= 0 then a^2 < b^2

    e.g. am I within 50 units of the origin (i.e. within a circle of radius 50) -> is the length squared < 50 * 50
  • edited July 2013
    I've written routines like this more times than I care to remember as I write a lot of bagatelle/pinball simulations at work. Trust me, ATN is unfit for this purpose.

    This is basically how the towers in Blizzard's Rift work out which direction to fire at the player:

    If x is your horizontal distance and y is your vertical distance then:

    LET h=SQR (x*x+y*y)

    will give you the length of the hypotenuse. Then:

    LET a=ACS (x/h) should give you the angle, which you can then add to/subtract from the relevant compass point depending on the quarter you're in.

    Obviously, Blizzard's Rift does all of this in machine code but the principle is the same.
    Still supporting Multi-Platform Arcade Game Designer, currently working on AGD 5. I am NOT on Twitter.
    Egghead Website
    Arcade Game Designer
    My itch.io page
  • edited July 2013
    And you don't use vectors either??? :-o

    EDIT: I've never had a problem using atan2 in C/C++, dunno if it is different on 8-bit platforms though (PC/PS1/PS2/PS3/PSVita/XBox360/Wii are the platforms I have worked on before). EDIT: Actually I would be using atan2f since that takes and returns a float rather than a double which is what atan2 does. PS1 was fixed point 4.12 (4 bits of integral value [20 if using 32 bit integer fixed point] and 12 bits of fractional values (i.e. 1/4096 accuracy)), and I had no problems with atan2 there either...
  • edited July 2013
    Thanks guys, im thinking about making an asteroids type of game with some intelligent alien ships, so this type of code will be invaluable :D
    And that is in any programming language :)
  • edited July 2013
    For asteroids, the only trig you would need to use is if you are rotating the ship at an arbitrary angle and it is made up of line segments (you would use sin, cos to rotate the lines around the centre of rotation). Everything else you can do with 2D vectors for position and velocity of the bullets, asteroids, player etc. (ok, you may use some trig to determine which way the asteroids fly apart when they split up after being hit by a bullet, maybe...).

    EDIT: Ok again, you'd also use trig to rotate the facing vector of the player ship when you use rotate left and right buttons ;)
Sign In or Register to comment.