raw math

With my last HTML5 experiment "Space Shooter", I had to solve a problem of checking, if an angle is between two other angles. Sounds easy for the first moment, but let's see, if there is not the one or the other portion of trouble in it.

According to the sketch above, we want to check, if the angle of the red vector is between the two blue vectors - all vectors have the same origin and same length. We could simply check the condition, if blue1 < red < blue2. To make this condition working, all angles have to be in the same interval, namely [0, 360). But if you've seen the experiment, every circle can be rotated in every direction, and also the space-ship can be positioned in every constellation. So, how would you check, if -894° is between 7000 and 7050°? Right, we must normalize the angles. The most intuitive way would be

while (angle < 0)
	angle+= 360;
while (angle >= 360)
	angle-= 360;

But we can do even better. Positive angles can be normalized by a modulo operation by 360. The problem are negative values. We could fall back to the loop approach, described above, but I try to avoid branching code. I thought a while about the problem, and a mathematical solution isn't hard to find:

(360 + angle % 360) % 360

With the first modulo operation, we reduce the number to an interval of (-360, 360). This is not what we need, so moving the negative part to the positive, and truncate again brings the desired result.

We could even optimize a bit more, if we know, that the used data-type is limited and our value is not arbitrarily small. So, integer is limited to 231 and 232 in the unsigned version, respectively. Choosing a middle large divisor of 360, which suffices the needs, saves one division:

(3600000 + angle) % 360

Back to the main problem, of deciding, if an angle is between two other angles. Now, we have a better start situation for the decision if an angle of an vector is between two other angles, but consider the following sketch:

In this situation, a check if blue1 < red < blue2 will ever return false. We have to bring another rule into play. I described this case as, if blue2< blue1, check from 0 to blue2 and from blue1 to 360. That's the whole magic. The complete implementation as a JavaScript function looks like this:

function angle_between(n, a, b) {
	n = (360 + (n % 360)) % 360;
	a = (3600000 + a) % 360;
	b = (3600000 + b) % 360;

	if (a < b)
		return a <= n && n <= b;
	return a <= n || n <= b;
}