atan2

Since trig and inverse trig come up time and time again, I spent a few minutes tossing together a reasonable atan2 approximation. Making good approximations require more work that I’m willing to commit to this function. (Because you really shouldn’t need to call this very much: says the old grump).


private static final float PI        = (float)(Math.PI);
private static final float PI_OVER_2 = (float)(Math.PI/2);
private static final float PI_OVER_4 = (float)(Math.PI/4);

/** Return 'x' multiplied by the sign of 'y'. */
// copySign isn't yet an intrinsic, so this isn't a desirable function to call.
public static float mulsign(float x, float y) { return (Math.copySign(1, y) * x); }

/**
 * arc tangent of x on [-1,1]
 * <p>
 * 5th order minimax approximation (minimizing abs error).  Banged out quickly,
 * a better version could be produced.  Error bound of ~0.0006 @
 * x = +/-{0.205219, 0.59347, 0.888196, 1.0}
 */
public static final float atan_5(float x)
  {
    // quality (of either abs or rel error) can be improved at same cost.
    // Additionally (by using Estrin's) one could probably produce a higher
    // order approximation at same cost (by reducing dependency chains). YMMV.
    float x2 = x*x;
    return x*(0.995354f+x2*(-0.288769f+0.079331f*x2));
  }

/** */
public static final float atan(float y, float x)
{
  float ay = Math.abs(y);
  float xy = x*y; // this is xor of sign of x & y...probably removable.
  float r  = 0;
    
  // perform argument reduction.  probably can be reduced
  if (x  < 0) { x = -x; r = -PI; }
  if (ay > x) { float t = x; x = ay; ay = -t; r += PI_OVER_2; }
    
  // perform the approximation..increasing speed or accuracy by 
  // trading the base approximation possible
  r += atan_5(ay/x);

  // xor the sign of reduced range with the input for final result..
  // again all of this could probably be cleaned up with some thought.
  return mulsign(r, xy);
}

  /**
   * atan of x on [0,Inf]
   */
  public static final float atanp(float x)
  {
    // ignoring numeric issues around '1' due to the subtract,
    // same error bound as core routine, stretched out over
    // the interval.
    return PI_OVER_4 + atan_5((x-1)/(x+1));
  }
  
  /** atan of x on [-Inf, Inf] */
  public static final float atan(float x)
  {
    float r = atanp(Math.abs(x));
    
    return Math.copySign(r, x);
  }

It should be pointed out that using either complex numbers, Quaternions and vectors, you can in fact avoid all trig that is not a constant. Well i think it should be pointed out.
[edit for Riven the grammar police.]

Can you use that sentence in a sentence please? :persecutioncomplex:

Yeah, delt0r’s comment is what I’m roughly saying. It should be very rare to actually need to call trig and inverse trig functions in a video game. Especially if you’re using fixed rate simulation, which you should be doing for a huge list of reasons (in my opinion).

I use it all the time because I last looked at the alternative method with complex numbers over 25 years ago and have subsequently totally forgotten it all :S

Would you briefly describe how I’d find the angle of a bullet given its velocity (dx, dy)?
Or given an angle, how I’d calculate its direction?
I have a feeling this is a fundamental brain-change required on my part…

Cas :slight_smile:

This.

BTW, You can directly calculate the vector components between two points via a normalized vector but that would mean an sqrt() and I would assume that’s slower than a simple atan2().

A dot product should also work, but that would still use an acos().

You don’t find the angle because you don’t need it. Just a vector in the direction of travel. So point at etc are all just x,y coordinates its easy to find the vector pointing in that direction. Now assume x is imaginary (or is it y) and complex multiplication is adding angles. Angles you never need to know. Normalization is much faster than trig and esp atan2. Sqrt are pretty fast in comparison to trig functions. Done right you can avoid to much normalization too.

Really trig functions are pretty expensive and inverse trig even more so.

In 3D yes, because you could just align your objects in an orthogonal matrix. And a normalized direction vector would be faster and simpler because in 3D, spherical coordinates need 2 angles (azimuth and elevation) and 5 trig functions to convert to rectangular coordinates. But in a 2D game, I still could not find a better way to align my rotated sprites without the use of *atan2().

*Excepting bezier splines, catmull-rom splines, etc. which you can get the component vectors by finding the differential.

I have code that does it. Its easier in 2D. I could share it in the code section?

Sure. I would love to see it. ;*)

Because I’m nuts, I stared a wiki page: Complex number cookbook. Go ask questions. Or better yet…add content!

Just added a new shared code post with my complex number thingy. http://www.java-gaming.org/topics/complex-angles-why-you-don-t-need-trig/28992/view.html

Added some notes and single parameter methods atanp & atan.