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...
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...)
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:
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:
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.-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
.-0
negative in order to make it work properly. So, instead of checking dX < 0
and radians < 0
, we check their inverse.NaN
in case both dX
and dY
are 0
(or -0
).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:
atan2
function has been created to automatically handle all but one case of the many possible cases, and(-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.
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 + '°' + ' (' + 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>
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.
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With