Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get angle in terms of 360 degrees

I would like to get an angle in terms of 360 degrees... for my game, I need to know which direction the player is heading in...

enter image description here

The code here gets the proper angles, but only in terms of 90 degree increments: (meaning, when I click in upper left quadrant, I get angle from 0 to 90 degrees... bottom left is 0 to -90 degrees, etc...)

enter image description here

    var dY = this.pos.y-e.gameY;            //opposite
    var dX = this.pos.x-e.gameX;            //adjacent
    var dist = Math.sqrt((dY*dY)+(dX*dX));  //hypotenuse
    var sin = dY/dist;                      //opposite over hypotenuse
    var radians = Math.asin(sin);
    var degrees = radians*(180/Math.PI);    //convert from radians to degrees
    this.calculatedAngle = degrees;     

How can I get it in terms of 360 degrees?


Here is another example: The top two represent the issue... when I click in the upper/lower left quadrant, it keeps drawing a right triangle from the x axis...

I need it to be like the lower 2 pictures, where it keeps drawing the angle around:

enter image description here

like image 406
user3871 Avatar asked Jan 29 '15 23:01

user3871


3 Answers

Try this:

var dY = this.pos.y - e.gameY,          // opposite
    dX = this.pos.x - e.gameX,          // adjacent
    radians = Math.atan(dY/dX);         // wrong, in [-1/2 pi, 1/2 pi]
if(1/dX < 0) radians += Math.PI;        // fixed, in [-1/2 pi, 3/2 pi]
if(1/radians < 0) radians += 2*Math.PI; // fixed, in [+0, 2 pi]
var degrees = radians*180/Math.PI;      // from radians to degrees

Explanation:

  • Better calculate the radians with Math.atan and the tangent. Calculating the hypotenuse using Math.sqrt is expensive.
  • Math.atan gives an angle between -1/2 pi and 1/2 pi, that is, the right half circle. To fix it, just sum pi in case dX was negative.
  • Then we get an angle between -1/2 pi and 3/2 pi. So, in case it's negative, we sum 2 pi in order to obtain an angle between 0 and 2 pi.
  • Note that we must consider -0 negative in order to make it work properly. So, instead of checking dX < 0 and radians < 0, we check their inverse.
  • Note the final result will be NaN in case both dX and dY are 0 (or -0).
like image 85
Oriol Avatar answered Nov 15 '22 13:11

Oriol


You can do this directly from the coordinates, without computing extra information like the hypotenuse, by using the atan2 function, which was designed in the early days of FORTRAN for situations exactly like yours.

Note two important things:

  1. that the atan2 function has been created to automatically handle all but one case of the many possible cases, and
  2. it will output a range of (-PI, PI].

The case where both coordinates are (0, 0) is left undefined (all angles are equivalent when the magnitude of a vector is zero), so I'm arbitrarily setting the angle to zero degrees in that case. And in order to obtain the desired range, some simple logic and addition is needed.

var Vx = this.pos.x - e.gameX;
var Vy = this.pos.y - e.gameY;

var radians;

if (Vx || Vy) {
    radians = Math.atan2(Vy, Vx);
} else {
    radians = 0;
}

if (radians < 0) {
    radians += 2*Math.PI;
}

var degrees = radians * 180 / Math.PI;

this.calculatedAngle = degrees;

The result will be an angle defined for all cases and within the range [0, 360°), as desired.

Example

function showDegrees(e, svg) {
	var rectangle = svg.getBoundingClientRect();
	var targetX = (rectangle.left + rectangle.right)/2;
	var targetY = (rectangle.top + rectangle.bottom)/2;
	var Vx = Math.round(e.clientX - targetX);
	var Vy = Math.round(targetY - e.clientY);
	var radians = Math.atan2(Vy, Vx);
	if (radians < 0) radians += 2*Math.PI;
	var degrees = Math.round(radians*180/Math.PI);
	var textBox = document.getElementById('showdegrees');
	textBox.innerHTML = degrees + '&deg;' + ' (' + Vx + ', ' + Vy + ')';
	textBox.setAttribute('x', Math.round(100 + Vx));
	textBox.setAttribute('y', Math.round(100 - Vy));
}
<svg width="200" height="200" viewBox="0 0 200 200" onmousemove="showDegrees(evt, this)">
<text x="0" y="0" fill="red" style="font-size: 12px"  id="showdegrees">Info</text>
<line x1="100" y1="0" x2="100" y2="200" style="stroke: black; stroke-width: 1" />
<line x1="0" y1="100" x2="200" y2="100" style="stroke: black; stroke-width: 1" />
</svg>
like image 36
Joseph Myers Avatar answered Nov 15 '22 15:11

Joseph Myers


You need to use a little bit more information than just the arcsin to figure this out. The reason for this is that arcsin will only return values between -π/2 and π/2 (-90 degrees and 90 degrees), as you already know.

So, to figure out the other part, you need to be aware of what quadrant you are in.

enter image description here

In quadrant 2, arcsin is between 90 and 0, the real angle is between 90 and 180. So if you are in quadrant 2, 180-calculated angle=real angle. (180-90=90, 180-0=180).

In quadrant 3, arcsin is between 0 and -90. The real angle is between 180 and 270. so again, 180-calculated angle = real angle. (180-0=180, 180-(-90)=270).

In quadrant 4, arcsin is between -90 and 0. The real angle is between 270 and 360. So 360+calculated angle=real angle. (360+(-90)=270, 360+0)=360).

The same is also true for quadrant 1, you just don't need to make the transformation. (0+360=360 (equivalent to 0), 90+360=450 (equivalent to 90) ).

So, determine the quadrant first, and apply a rule based on that. For an (x,y) coordinate, if x is positive and y is positive, you are quadrant 1. If x is negative and y is positive, you are quadrant 2. If x is negative and y is negative, you are quadrant 3. if x is positive and y is negative, you are quadrant 4.

So in your case, the distance from the origin is you (dX,dY) coordinate pair, so something like this should do it:

//your original code...
var degrees = radians*(180/Math.PI);    //convert from radians to degrees

//then


if (dX>=0 and dY>=0)
{//quadrant 1
//no transformation, do nothing
}
if (dX>=0 and dy<0) 
{ //quadrant 4
degrees=360+degrees;
}
if (dX<0 and dy<0) 
{ //quadrant 3
degrees=180-degrees;
}
if (dX<0 and dy>0) 
{ //quadrant 2
degrees=180-degrees;
}


this.calculatedAngle = degrees;
like image 36
chiliNUT Avatar answered Nov 15 '22 15:11

chiliNUT