1

I have a normalized 2D vector that I am using to rotate other 2D vectors. In one instance it indicates "spin" (or "angular momentum") and is used to rotate the "orientation" of a simple polygon. My vector class contains this method:

rotateByXY(x, y) {
  let rotX = x * this.x - y * this.y;
  let rotY = y * this.x + x * this.y;
  this.x = rotX;
  this.y = rotY;
}

So far, this is all efficient and uses no trig whatsoever.

However, I want the "spin" to decay over time. This means that the angle of the spin should tend towards zero. And here I'm at a loss as to how to do this without expensive trig calls like this:

let angle = Math.atan2(spin.y, spin.x);
angle *= SPIN_DECAY;
spin = new Vector2D(Math.cos(angle), Math.sin(angle));

Is there a better/faster way to accomplish this?

Scott Schafer
  • 487
  • 3
  • 14
  • 1
    Simple formulas exist only for `SPIN_DECAY = 1/2` (and for arithmetic progression `an[i] = an0 - i*da`). Otherwise it would better to store `angle` to avoid `atan2`. If you apply the same rotation to multiple points, single evaluation of sin/cos per step is not so bad. – MBo Jan 04 '19 at 05:35
  • 1
    what about this: [How to calculate rocket?](https://stackoverflow.com/a/53986426/2521214) see the rotation math at the end... beware it works only for rotations up to `90deg` and the rotation is not linear but not too far from it either and works for any dimensionality...the dimming is done simply by decreasing the `dang0` or by `dang0*=0.95` each iteration etc ... you do not need the `acos` for computing `a` as you already know how much your original rotation rotates ... – Spektre Jan 04 '19 at 11:00
  • Another idea: what about approximating your trigonometric functions with their Taylor expansions? – Dominik Mokriš Jan 17 '19 at 20:03
  • @DominikMokriš, that sounds intriguing. Can you say more and/or provide details? Not quite sure how to implement that. BTW, I did consider something like this lookup: int iX = int((spin.x + 1) * 100); int iY = int((spin.y + 1) * 100); spin = DecayLookupX[iX][iY]; Where DecayLookup is a 200 x 200 array of pre-calculated vectors (could be tuned). A bit rough, but would work for a constant decay. – Scott Schafer Jan 19 '19 at 00:28

1 Answers1

2

If it's really the trigonometric functions what is slowing down your computation, you might try to approximate them with their Taylor expansions.

For x close to zero the following identities hold:

cos(x) = 1 - (x^2)/2! + (x^4)/4! - (x^6)/6! + ...
sin(x) = x - (x^3)/3! + (x^5)/5! - (x^7)/7! + ...
atan(x) = x - (x^3)/3 + (x^5)/5 - (x^7)/7 + ...

Based on the degree of accuracy you need for your application you can trim the series. For instance,

cos(x) = 1 - (x^2)/2

with an error of the order of x^3 (actually, x^4, as the term with x^3 is zero anyway).

However, I don't think that this is going to solve your problem: the actual implementation of atan is likely to be already using the same trick, written by someone with lots of experience of speeding these things up. So this is not really a proper answer but I hope it could still be useful.

Dominik Mokriš
  • 1,118
  • 1
  • 8
  • 29
  • Ah. Thanks for that explanation. This might be worth looking into. Although since my decay should be constant I think I might be able to get away with a pre-calculated lookup table. Appreciate it though! – Scott Schafer Jan 23 '19 at 00:57