75

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?

2
  • 2
    You need at least three points to calculate an angle. Which is you 3rd? The x-axis or y-axis? Commented Mar 8, 2012 at 7:19
  • 1
    See also: Same question for Python Commented Jun 20, 2020 at 17:34

5 Answers 5

155

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;
}
Sign up to request clarification or add additional context in comments.

7 Comments

Note that to avoid division by 0, you should test if dx == 0 first; if it is, then return 90 degrees if dy > 0 and 270 degrees if dy < 0.
@EtiennePerot: I remembered another useful function and updated my answer to use that instead.
You may also want to convert the result to degrees, it sounds like that's what the OP wants.
Almost, there is a little error in it: theta*=180/Math.PI is correct. You also don't need to worry about zero division, when using atan2(), it returns correct value with 0 too.
Added (optional) code to flip it to 360 range as the question asked.
|
26

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>

Comments

22

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;
}

6 Comments

What does it mean when I have to add 90 degrees to the answer to get the correct rotation?
Also, what are the names of the coordinate systems? Cartesian coordinates mover right and up en.wikipedia.org/wiki/Cartesian_coordinates. What is it called when positive values move right and down?
@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.
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.21gigawatts, same. Off by 90°.
|
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;
    }

Comments

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)))

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.