54

I want to get a simple solution to calculate the angle of a line (like a pointer of a clock).

I have 2 points:

cX, cY - the center of the line.
eX, eY - the end of the line.

The result is angle (0 <= a < 360).

Which function is able to provide this value?

Purag
  • 16,273
  • 4
  • 48
  • 70
durumdara
  • 3,071
  • 4
  • 40
  • 66

5 Answers5

119

You want the arctangent:

dy = ey - cy
dx = ex - cx
theta = arctan(dy/dx)
theta *= 180/pi // rads to degs

Erm, note that the above is obviously not compiling Javascript code. You'll have to look through documentation for the arctangent function.

Edit: Using Math.atan2(y,x) will handle all of the special cases and extra logic for you:

function angle(cx, cy, ex, ey) {
  var dy = ey - cy;
  var dx = ex - cx;
  var theta = Math.atan2(dy, dx); // range (-PI, PI]
  theta *= 180 / Math.PI; // rads to degs, range (-180, 180]
  //if (theta < 0) theta = 360 + theta; // range [0, 360)
  return theta;
}
akinuri
  • 7,673
  • 10
  • 47
  • 80
Christian Mann
  • 7,410
  • 4
  • 38
  • 51
20

Runnable version of Christian's answer.

function angle(cx, cy, ex, ey) {
  var dy = ey - cy;
  var dx = ex - cx;
  var theta = Math.atan2(dy, dx); // range (-PI, PI]
  theta *= 180 / Math.PI; // rads to degs, range (-180, 180]
  return theta;
}
function angle360(cx, cy, ex, ey) {
  var theta = angle(cx, cy, ex, ey); // range (-180, 180]
  if (theta < 0) theta = 360 + theta; // range [0, 360)
  return theta;
}

show("right", 0, 0, 1, 0);
show("top right", 0, 0, 1, 1);
show("top", 0, 0, 0, 1);
show("top left", 0, 0, -1, 1);
show("left", 0, 0, -1, 0);
show("bottom left", 0, 0, -1, -1);
show("bottom", 0, 0, 0, -1);
show("bottom right", 0, 0, 1, -1);

// IGNORE BELOW HERE (all presentational stuff)
table {
  border-collapse: collapse;
}
table, th, td {
  border: 1px solid black;
  padding: 2px 4px;
}
tr > td:not(:first-child) {
  text-align: center;
}
tfoot {
  font-style: italic;
}
<table>
  <thead>
    <tr><th>Direction*</th><th>Start</th><th>End</th><th>Angle</th><th>Angle 360</th></tr>
  </thead>
  <tfoot>
     <tr><td colspan="5">* Cartesian coordinate system<br>positive x pointing right, and positive y pointing up.</td>
  </tfoot>
  <tbody id="angles">
  </tbody>
</table>
<script>
function show(label, cx, cy, ex, ey) {
  var row = "<tr>";
  row += "<td>" + label + "</td>";
  row += "<td>" + [cx, cy] + "</td>";
  row += "<td>" + [ex, ey] + "</td>";
  row += "<td>" + angle(cx, cy, ex, ey) + "</td>";
  row += "<td>" + angle360(cx, cy, ex, ey) + "</td>";
  row += "</tr>";
  document.getElementById("angles").innerHTML += row;
}
</script>
Community
  • 1
  • 1
TWiStErRob
  • 38,918
  • 20
  • 146
  • 220
8

If you're using canvas, you'll notice (if you haven't already) that canvas uses clockwise rotation(MDN) and y axis is flipped. To get consistent results, you need to tweak your angle function.

From time to time, I need to write this function and each time I need to look it up, because I never get to the bottom of the calculation.

While the suggested solutions work, they don't take the canvas coordinate system into consideration. Examine the following demo:

Calculate angle from points - JSFiddle

function angle(originX, originY, targetX, targetY) {
    var dx = originX - targetX;
    var dy = originY - targetY;

    // var theta = Math.atan2(dy, dx);  // [0, Ⲡ] then [-Ⲡ, 0]; clockwise; 0° = west
    // theta *= 180 / Math.PI;          // [0, 180] then [-180, 0]; clockwise; 0° = west
    // if (theta < 0) theta += 360;     // [0, 360]; clockwise; 0° = west

    // var theta = Math.atan2(-dy, dx); // [0, Ⲡ] then [-Ⲡ, 0]; anticlockwise; 0° = west
    // theta *= 180 / Math.PI;          // [0, 180] then [-180, 0]; anticlockwise; 0° = west
    // if (theta < 0) theta += 360;     // [0, 360]; anticlockwise; 0° = west

    // var theta = Math.atan2(dy, -dx); // [0, Ⲡ] then [-Ⲡ, 0]; anticlockwise; 0° = east
    // theta *= 180 / Math.PI;          // [0, 180] then [-180, 0]; anticlockwise; 0° = east
    // if (theta < 0) theta += 360;     // [0, 360]; anticlockwise; 0° = east

    var theta = Math.atan2(-dy, -dx); // [0, Ⲡ] then [-Ⲡ, 0]; clockwise; 0° = east
    theta *= 180 / Math.PI;           // [0, 180] then [-180, 0]; clockwise; 0° = east
    if (theta < 0) theta += 360;      // [0, 360]; clockwise; 0° = east

    return theta;
}
akinuri
  • 7,673
  • 10
  • 47
  • 80
  • 1
    What does it mean when I have to add 90 degrees to the answer to get the correct rotation? – 1.21 gigawatts Aug 15 '19 at 21:37
  • Also, what are the names of the coordinate systems? Cartesian coordinates mover right and up https://en.wikipedia.org/wiki/Cartesian_coordinates. What is it called when positive values move right and down? – 1.21 gigawatts Aug 15 '19 at 21:41
  • @1.21gigawatts Are you quoting me with _"have to add 90 degrees to the answer to get the correct rotation"_ (because I didn't say such a thing) or is it something else and you're trying to make sense of it? Hmm, I'm not sure if it has a name. The only difference between the two is the direction of the rotation. Canvas is also using cartesian coordinate system. It's just clockwise instead of counter-clockwise. Examine the "Quadrants and octants" section in the page you linked. Flip the `y` axis, that is switch `I` and `II` quadrants with `IV` and `III`, respectively. Now, it's clockwise. – akinuri Aug 16 '19 at 07:25
  • 2
    I'm trying to make sense of why adding 90 deg to the result works for my case. I just noticed in @pasx answer he has a comment `a -= (Math.PI/2); //shift by 90deg` and I've seen mention of 90deg else where. – 1.21 gigawatts Aug 16 '19 at 14:33
  • @1.21gigawatts, same. Off by 90°. – Jeff Fischer Sep 24 '20 at 17:34
1

One of the issue with getting the angle between two points or any angle is the reference you use.

In maths we use a trigonometric circle with the origin to the right of the circle (a point in x=radius, y=0) and count the angle counter clockwise from 0 to 2PI.

In geography the origin is the North at 0 degrees and we go clockwise from to 360 degrees.

The code below (in C#) gets the angle in radians then converts to a geographic angle:

    public double GetAngle()
    {
        var a = Math.Atan2(YEnd - YStart, XEnd - XStart);
        if (a < 0) a += 2*Math.PI; //angle is now in radians

        a -= (Math.PI/2); //shift by 90deg
        //restore value in range 0-2pi instead of -pi/2-3pi/2
        if (a < 0) a += 2*Math.PI;
        if (a < 0) a += 2*Math.PI;
        a = Math.Abs((Math.PI*2) - a); //invert rotation
        a = a*180/Math.PI; //convert to deg

        return a;
    }
pasx
  • 1,958
  • 21
  • 21
1

You find here two formulas ,one from positive axis x and anticlockwise

and one from the north and clockwise.

There is x=x2-x1 and y=y2=y1 .There is E=E2-E1 and N=N2-N1.

The formulas are working for any value of x,y, E and N.

For x=y=0 or E=N=0 the result is undefined.

f(x,y)=pi()-pi()/2*(1+sign(x))*(1-sign(y^2))

     -pi()/4*(2+sign(x))*sign(y)

     -sign(x*y)*atan((abs(x)-abs(y))/(abs(x)+abs(y)))

f(E,N)=pi()-pi()/2*(1+sign(N))*(1-sign(E^2))

     -pi()/4*(2+sign(N))*sign(E)

     -sign(E*N)*atan((abs(N)-abs(E))/(abs(N)+abs(E)))