How to calculate an angle from points?
Asked Answered
W

5

74

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?

Wallin answered 8/3, 2012 at 7:16 Comment(2)
You need at least three points to calculate an angle. Which is you 3rd? The x-axis or y-axis?Heel
See also: Same question for PythonFoliaceous
D
152

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;
}
Daisy answered 8/3, 2012 at 7:19 Comment(7)
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.Windy
@EtiennePerot: I remembered another useful function and updated my answer to use that instead.Daisy
You may also want to convert the result to degrees, it sounds like that's what the OP wants.Windy
Is this function returns Angle?Wallin
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.Burkhard
Gah, so close! My math major is truly paying dividends.Daisy
Added (optional) code to flip it to 360 range as the question asked.Mariselamarish
M
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>
Mariselamarish answered 30/6, 2015 at 11:3 Comment(0)
C
21

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;
}
Coit answered 5/12, 2017 at 12:27 Comment(7)
What does it mean when I have to add 90 degrees to the answer to get the correct rotation?Kosse
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?Kosse
@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.Coit
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.Kosse
@1.21gigawatts, same. Off by 90°.Protohuman
@1.21gigawatts It might be because you're thinking of starting rotation/position in terms of the starting angle being "forward" or "up" rather than starting rotation position being along the x-axisGillan
Thanks this is a great answer, cheersTwerp
O
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;
    }
Ochone answered 3/12, 2016 at 14:45 Comment(0)
T
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)))
Tiga answered 13/11, 2018 at 14:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.