More efficent ways to get PVector heading or atan2

Hi,

I’ve been experimenting on some steering behaviors, and some of the functions need the velocities heading, but it seemed that PVector.heading() takes up a lot of CPU as it uses atan2() and makes the sketch run slower.

In a sketch with 5000 boids, .heading() uses over 30% of the CPU (based on profiler info). Is there any way to optimize the function, or to have it more efficient with around the same results? (It doesn’t need it to be so accurate)

Any questions or advice are welcome,
Thanks!

2 Likes

I’ve found this supposedly faster but inaccurate atan2() alternative at: :man_scientist:

And adapted it as a custom subclass named VecFast, which @Override PVector::heading() method: :star_struck:

/**
 * Atan2Fast() for PVector::heading() (v1.0.4)
 * GoToLoop (2018/Oct/21)
 *
 * https://Discourse.Processing.org/t/
 * more-efficent-ways-to-get-pvector-heading-or-atan2/4706/2
 */

//import processing.core.VecFast;

void setup() {
  final PVector vecFast = new VecFast(THIRD_PI, TAU);
  final PVector vecSlow = vecFast.get();

  println("Fast:", vecFast.heading()); // Fast: 1.405416
  println("Slow:", vecSlow.heading()); // Slow: 1.4056476

  exit();
}

//package processing.core; // Comment this out in "VecFast.java".

static // Comment this keyword if this class is placed in "VecFast.java".
  public final class VecFast extends PVector {
  public VecFast() {
  }

  public VecFast(final float x, final float y) {
    super(x, y);
  }

  public VecFast(final float x, final float y, final float z) {
    super(x, y, z);
  }

  @Override public final float heading() {
    return atan2Fast(y, x);
  }

  /*
   * Apache 2 (2011): https://libGDX.BadLogicGames.com/source.html
   *
   * https://GitHub.com/libgdx/libgdx/blob/gdx-parent-1.9.8/
   * gdx/src/com/badlogic/gdx/math/MathUtils.java#L84-L100
   *
   * Mod: GoToLoop (v1.0) (2018/Oct/21)
   * https://Discourse.Processing.org/t/
   * more-efficent-ways-to-get-pvector-heading-or-atan2/4706/2
   */

  /**
   * Returns atan2 in radians, faster but less accurate than Math.atan2.
   * Average error of 0.00231 radians (0.1323 degrees),
   * largest error of 0.00488 radians (0.2796 degrees).
   */
  static public final float atan2Fast(final float y, final float x) {
    if (x == 0f)  return HALF_PI * Math.signum(y);

    final float z = y / x, atan;

    if (Math.abs(z) < 1f) {
      atan = z / (z * z * .28f + 1f);
      return x >= 0f? atan : atan + (y < 0f? -PI : PI);
    }

    atan = HALF_PI - z / (z * z + .28f);
    return y >= 0f? atan : atan - PI;
  }
}
5 Likes

Thanks so much, it works perfectly! Also never knew you could override classes like that. :slight_smile:

1 Like