11

To increase the speed at which I find the sine/cosine of an angle, I have built a reference table instead of calculating them on the fly. I have the same idea with finding the angle from one point to another.

I have created a table of 3600 normalized vectors (3600 / 10 = accuracy of one tenth of a degree). Whenever I need to know the angle from one point to the next, I look through the table to find the best match. However, I am concerned that this might be slower than using math.atan2().

Here is the code I am using:

Create the vector table:

// vector to angle table
var vectorToAngleTable = new Array();
for (i = 0; i < 3600; i += 1) {
    vectorToAngleTable[i] = new Vector2();
    vectorToAngleTable[i] = RotatePoint(forwardVector, i / 10);
}

Find the angle from two points:

function NormalizeVector(vector) {
    var toReturn = vector;
    var dist = Math.sqrt(vector.x * vector.x + vector.y * vector.y);
    toReturn.x /= dist.x;
    toReturn.y /= dist.y;
    return toReturn;
}

function PointDirection(position, target) {
    var vector = target;
    var toReturn = 0;
    var smallest = 1.0;
    vector.x -= position.x;
    vector.y -= position.y;
    vector = NormalizeVector(vector);
    for (i = 0; i < 3600; i += 1) {
        if (PointDistance(vectorToAngleTable[i], vector) < smallest) {
            smalllest = PointDistance(vectorToAngleTable[i], vector);
            toReturn = i;
        }
    }
    return toReturn;
}

function PointDistance(point1, point2) {
    return Math.sqrt(((point2.x - point1.x) * (point2.x - point1.x)) + ((point2.y - point1.y) * (point2.y - point1.y)));
}

As you can see, my concern is all the lines of code it is going through, and how many entries there are on the table that it looks through. I would love to know the fastest way to find the angle, no matter what the method is.

Charles
  • 50,943
  • 13
  • 104
  • 142
Timothy Eckstein
  • 307
  • 1
  • 2
  • 10
  • 2
    Belongs on: http://codereview.stackexchange.com/ – Diodeus - James MacFarlane Oct 15 '12 at 20:35
  • 3
    Using built in functions is almost certainly faster (this being javascript). Especially because your method as it stands right now involves taking a square root, which is probably exactly the same in terms of efficiency as using the arctangent function, since sqrt is probably done with about 5 iterations of newtons approximation and arctan is probably done with the first few terms of an infinite series. – Wug Oct 15 '12 at 20:37
  • 2
    `toReturn.x /= dist.x; toReturn.y /= dist.y;` looks wrong. `dist` is a scalar, not another vector. – japreiss Oct 15 '12 at 20:39
  • very similar to http://stackoverflow.com/questions/1427422/cheap-algorithm-to-find-measure-of-angle-between-vectors – hatchet - done with SOverflow Oct 15 '12 at 20:40
  • also similar to http://stackoverflow.com/a/3441867/1615483 – Paul S. Oct 15 '12 at 20:41
  • If you haven't confirmed through profiling that the angle calculation is a bottleneck, just use the standard `angle = acos(dot(normalized(a), normalized(b)))` – japreiss Oct 15 '12 at 20:42
  • @Diodeus: No, this question is on-topic for this site, and strictly speaking does *not* meet all of the requirements for the codereview site. – RBarryYoung Oct 15 '12 at 21:16
  • If we had `RotatePoint` and `Vector2` we could test on jsperf – Paul S. Oct 15 '12 at 21:30
  • @shhac, I just looked up jsperf. Brilliant concept, but I trust experienced piers enough to take their word for it. If you want my code to play around with it yourself, I'll put it online for you and link to it. – Timothy Eckstein Oct 15 '12 at 22:05
  • @dergenialeein I wrote up a loop-over-array vs Math.acos thing (link at bottom of my answer) The reason I asked for your code was so the comparison was more true to your current implementation. You can see that the native acos method in JavaScript is much faster than a loop. – Paul S. Oct 16 '12 at 03:32

2 Answers2

13

As angle(v1, v2) = acos( (v1x * v2x + v1y * v2y) / (sqrt(v1x^2+v1y^2) * sqrt(v2x^2+v2y^2)) ) and we know v2 = [1, 0]

var v = {x: 0, y: 1},
    angleRad = Math.acos( v.x / Math.sqrt(v.x*v.x + v.y*v.y) ),
    angleDeg = angleRad * 180 / Math.PI;

where v is the vector [point2.x - point1.x , point2.y - point1.y]


Edit - I just realised you may have meant treat each point as a vector, in which case it'd be

var v1 = {x: 0, y: 1}, v2 = {x: 1, y: 0},
    angleRad = Math.acos( (v1.x * v2.x + v1.y * v2.y) / ( Math.sqrt(v1.x*v1.x + v1.y*v1.y) * Math.sqrt(v2.x*v2.x + v2.y*v2.y) ) ),
    angleDeg = angleRad * 180 / Math.PI;

where v1 is the vector [point1.x , point1.y] and v2 is [point2.x , point2.y]


Edit 2
To speed up if you're using the vectors length many times, save it as e.g. v.length = ... so you can get it without re-calculating again. If you know every vector will need it's angles calculated multiple times, use the first method I wrote and cache it, i.e. v.angle = .... You can then you can do v2.angle - v1.angle to find angle between the two, etc.
i.e. have

function Vector(x, y){
    this.x = x;
    this.y = y;
    this.length = Math.sqrt(x*x + y*y);
    this.angle = Math.acos( x / this.length );
}

jsperf of pre-computing and finding in an array of 3601 items vs using acos directly

Community
  • 1
  • 1
Paul S.
  • 64,864
  • 9
  • 122
  • 138
  • Wow, I am impressed. The difference is night and day. I did not think it would be so dramatic a difference. Anyway, thank you again shhac. – Timothy Eckstein Oct 16 '12 at 07:32
  • @dergenialeein Someone [modified it](http://jsperf.com/shhacos-vs-array/2) to use a polynomial approximation which lead me [**here**](http://stackoverflow.com/a/3380723/1615483), which is twice as fast again. Note that this may have bigger error though. – Paul S. Oct 16 '12 at 13:21
  • @shhac: That was me, I just put 'acos approximation' into Our Glorious Search Engine Overlord. Surprised that it was that much quicker, to be honest. – Phil H Oct 16 '12 at 14:59
  • 1
    hmm, a possible 11 degrees of error vs. 6 times slower, which is the greater evil? I guess if the game tokens are calculating their target direction often enough it could (for the most part) compensate for errors. Thank you shhac and Phil H, this is the kind of stuff I'm looking for. – Timothy Eckstein Oct 16 '12 at 20:00
1

That's definitely going to be smaller than a call to atan2, since it's a square root and then a linear search through 3600 possibilities. Conversely many processors implement atan2 directly — it's FPATAN in Intel land.

Tommy
  • 99,986
  • 12
  • 185
  • 204
  • Thank you Tommy. I knew that atan is a very common calculation and I thought it strange that none of the trig functions were calculated directly through the processor. If this is true then I assume it is also true for sine and cosine? That is, just using them is better than making a reference table. – Timothy Eckstein Oct 15 '12 at 21:48