如何计算直线与横轴的夹角?

时间:2021-11-04 16:57:34

In a programming language (Python, C#, etc) I need to determine how to calculate the angle between a line and the horizontal axis?

在编程语言(Python, c#,等等)中,我需要确定如何计算直线和横轴之间的夹角。

I think an image describes best what I want:

我认为一张图片最能描述我想要的:

如何计算直线与横轴的夹角?

Given (P1x,P1y) and (P2x,P2y) what is the best way to calculate this angle? The origin is in the topleft and only the positive quadrant is used.

给定(P1x,P1y)和(P2x,P2y)什么是计算这个角的最好方法?原点在左上角,只有正象限被使用。

7 个解决方案

#1


378  

First find the difference between the start point and the end point (here, this is more of a directed line segment, not a "line", since lines extend infinitely and don't start at a particular point).

首先找出起点和终点之间的区别(这里,这更多是一条有方向的线段,而不是一条“线”,因为线无限延伸,而不是从某个特定点开始)。

deltaY = P2_y - P1_y
deltaX = P2_x - P1_x

Then calculate the angle (which runs from the positive X axis at P1 to the positive Y axis at P1).

然后计算角度(从P1的正X轴到P1的正Y轴)。

angleInDegrees = arctan(deltaY / deltaX) * 180 / PI

But arctan may not be ideal, because dividing the differences this way will erase the distinction needed to distinguish which quadrant the angle is in (see below). Use the following instead if your language includes an atan2 function:

但是arctan可能并不理想,因为用这种方法划分差异将会消除区分哪个象限(见下文)所需要的区别。如果你的语言包括一个atan2函数,请使用以下方法:

angleInDegrees = atan2(deltaY, deltaX) * 180 / PI

EDIT (Feb. 22, 2017): In general, however, calling atan2(deltaY,deltaX) just to get the proper angle for cos and sin may be inelegant. In those cases, you can often do the following instead:

编辑(2017年2月22日):一般来说,打电话给atan2(deltaY,deltaX)只是为了得到合适的角度,因为cos和sin可能不雅。在这些情况下,你可以经常这样做:

  1. Treat (deltaX, deltaY) as a vector.
  2. 把(deltaX, deltaY)当作一个矢量。
  3. Normalize that vector to a unit vector. To do so, divide deltaX and deltaY by the vector's length (sqrt(deltaX*deltaX+deltaY*deltaY)), unless the length is 0.
  4. 将这个向量标准化到一个单位向量。为了做到这一点,将deltaX和deltaY除以向量的长度(sqrt(deltaX*deltaX+deltaY*deltaY)),除非长度为0。
  5. After that, deltaX will now be the cosine of the angle between the vector and the horizontal axis (in the direction from the positive X to the positive Y axis at P1).
  6. 在此之后,deltaX将是矢量和水平轴之间夹角的余弦(从正X方向到P1的正Y轴)。
  7. And deltaY will now be the sine of that angle.
  8. 而deltaY就是这个角的正弦值。
  9. If the vector's length is 0, it won't have an angle between it and the horizontal axis (so it won't have a meaningful sine and cosine).
  10. 如果向量的长度是0,它和横轴之间没有夹角(所以它不会有一个有意义的正弦和余弦)。

EDIT (Feb. 28, 2017): Even without normalizing (deltaX, deltaY):

编辑(2017年2月28日):即使不规范(deltaX, deltaY):

  • The sign of deltaX will tell you whether the cosine described in step 3 is positive or negative.
  • deltaX的符号将告诉你第3步中描述的cos是正的还是负的。
  • The sign of deltaY will tell you whether the sine described in step 4 is positive or negative.
  • deltaY的符号会告诉你第4步中描述的sin是正的还是负的。
  • The signs of deltaX and deltaY will tell you which quadrant the angle is in, in relation to the positive X axis at P1:
    • +deltaX, +deltaY: 0 to 90 degrees.
    • +deltaX +deltaY: 0到90度。
    • -deltaX, +deltaY: 90 to 180 degrees.
    • -deltaX, +deltaY: 90到180度。
    • -deltaX, -deltaY: 180 to 270 degrees (-180 to -90 degrees).
    • -deltaY: 180到270度(-180到-90度)。
    • +deltaX, -deltaY: 270 to 360 degrees (-90 to 0 degrees).
    • +deltaX, -deltaY: 270 - 360度(-90 - 0度)。
  • deltaX和deltaY的符号会告诉你哪个象限的角度,与P1的正X轴有关:+deltaX, +deltaY: 0到90度。-deltaX, +deltaY: 90到180度。-deltaY: 180到270度(-180到-90度)。+deltaX, -deltaY: 270 - 360度(-90 - 0度)。

An implementation in Python using radians (provided on July 19, 2015 by Eric Leschinski, who edited my answer):

使用radians的Python实现(由Eric Leschinski于2015年7月19日提供,他编辑了我的答案):

from math import *
def angle_trunc(a):
    while a < 0.0:
        a += pi * 2
    return a

def getAngleBetweenPoints(x_orig, y_orig, x_landmark, y_landmark):
    deltaY = y_landmark - y_orig
    deltaX = x_landmark - x_orig
    return angle_trunc(atan2(deltaY, deltaX))

angle = getAngleBetweenPoints(5, 2, 1,4)
assert angle >= 0, "angle must be >= 0"
angle = getAngleBetweenPoints(1, 1, 2, 1)
assert angle == 0, "expecting angle to be 0"
angle = getAngleBetweenPoints(2, 1, 1, 1)
assert abs(pi - angle) <= 0.01, "expecting angle to be pi, it is: " + str(angle)
angle = getAngleBetweenPoints(2, 1, 2, 3)
assert abs(angle - pi/2) <= 0.01, "expecting angle to be pi/2, it is: " + str(angle)
angle = getAngleBetweenPoints(2, 1, 2, 0)
assert abs(angle - (pi+pi/2)) <= 0.01, "expecting angle to be pi+pi/2, it is: " + str(angle)
angle = getAngleBetweenPoints(1, 1, 2, 2)
assert abs(angle - (pi/4)) <= 0.01, "expecting angle to be pi/4, it is: " + str(angle)
angle = getAngleBetweenPoints(-1, -1, -2, -2)
assert abs(angle - (pi+pi/4)) <= 0.01, "expecting angle to be pi+pi/4, it is: " + str(angle)
angle = getAngleBetweenPoints(-1, -1, -1, 2)
assert abs(angle - (pi/2)) <= 0.01, "expecting angle to be pi/2, it is: " + str(angle)

All tests pass. See https://en.wikipedia.org/wiki/Unit_circle

所有测试通过。参见https://en.wikipedia.org/wiki/Unit_circle

#2


47  

Sorry, but I'm pretty sure Peter's answer is wrong. Note that the y axis goes down the page (common in graphics). As such the deltaY calculation has to be reversed, or you get the wrong answer.

对不起,我很确定彼得的答案是错的。注意,y轴向下(在图形中常见)。因此,deltaY的计算必须被逆转,否则你会得到错误的答案。

Consider:

考虑:

System.out.println (Math.toDegrees(Math.atan2(1,1)));
System.out.println (Math.toDegrees(Math.atan2(-1,1)));
System.out.println (Math.toDegrees(Math.atan2(1,-1)));
System.out.println (Math.toDegrees(Math.atan2(-1,-1)));

gives

给了

45.0
-45.0
135.0
-135.0

So if in the example above, P1 is (1,1) and P2 is (2,2) [because Y increases down the page], the code above will give 45.0 degrees for the example shown, which is wrong. Change the order of the deltaY calculation and it works properly.

因此,如果在上面的例子中,P1(1,1)和P2(2,2)(因为Y增加了页面),上面的代码将给出示例显示的45.0度,这是错误的。更改deltaY计算的顺序,它可以正常工作。

#3


1  

I have found a solution in Python that is working well !

我已经在Python中找到了一个运行良好的解决方案!

from math import atan2,degrees

def GetAngleOfLineBetweenTwoPoints(p1, p2):
    return degrees(atan2(p2 - p1, 1))

print GetAngleOfLineBetweenTwoPoints(1,3)

#4


1  

Considering the exact question, putting us in a "special" coordinates system where positive axis means moving DOWN (like a screen or an interface view), you need to adapt this function like this, and negative the Y coordinates:

考虑到确切的问题,把我们放在一个“特殊的”坐标系中,正轴意味着向下移动(像屏幕或界面视图),你需要调整这个函数,像这样,负的Y坐标:

Example in Swift 2.0

例子在斯威夫特2.0

func angle_between_two_points(pa:CGPoint,pb:CGPoint)->Double{
    let deltaY:Double = (Double(-pb.y) - Double(-pa.y))
    let deltaX:Double = (Double(pb.x) - Double(pa.x))
    var a = atan2(deltaY,deltaX)
    while a < 0.0 {
        a = a + M_PI*2
    }
    return a
}

This function gives a correct answer to the question. Answer is in radians, so the usage, to view angles in degrees, is:

这个函数给出了这个问题的正确答案。答案是在弧度内,所以用角度来观察角度,是:

let p1 = CGPoint(x: 1.5, y: 2) //estimated coords of p1 in question
let p2 = CGPoint(x: 2, y : 3) //estimated coords of p2 in question

print(angle_between_two_points(p1, pb: p2) / (M_PI/180))
//returns 296.56

#5


0  

Based on reference "Peter O".. Here is the java version

参考“彼得O”。这是java版本。

private static final float angleBetweenPoints(PointF a, PointF b) {
float deltaY = b.y - a.y;
float deltaX = b.x - a.x;
return (float) (Math.atan2(deltaY, deltaX)); }

#6


0  

deltaY = Math.Abs(P2.y - P1.y);
deltaX = Math.Abs(P2.x - P1.x);

angleInDegrees = Math.atan2(deltaY, deltaX) * 180 / PI

if(p2.y > p1.y) // Second point is lower than first, angle goes down (180-360)
{
  if(p2.x < p1.x)//Second point is to the left of first (180-270)
    angleInDegrees += 180;
  else (270-360)
    angleInDegrees += 270;
}
else if (p2.x < p1.x) //Second point is top left of first (90-180)
  angleInDegrees += 90;

#7


0  

matlab function:

matlab函数:

function [lineAngle] = getLineAngle(x1, y1, x2, y2) 
    deltaY = y2 - y1;
    deltaX = x2 - x1;

    lineAngle = rad2deg(atan2(deltaY, deltaX));

    if deltaY < 0
        lineAngle = lineAngle + 360;
    end
end

#1


378  

First find the difference between the start point and the end point (here, this is more of a directed line segment, not a "line", since lines extend infinitely and don't start at a particular point).

首先找出起点和终点之间的区别(这里,这更多是一条有方向的线段,而不是一条“线”,因为线无限延伸,而不是从某个特定点开始)。

deltaY = P2_y - P1_y
deltaX = P2_x - P1_x

Then calculate the angle (which runs from the positive X axis at P1 to the positive Y axis at P1).

然后计算角度(从P1的正X轴到P1的正Y轴)。

angleInDegrees = arctan(deltaY / deltaX) * 180 / PI

But arctan may not be ideal, because dividing the differences this way will erase the distinction needed to distinguish which quadrant the angle is in (see below). Use the following instead if your language includes an atan2 function:

但是arctan可能并不理想,因为用这种方法划分差异将会消除区分哪个象限(见下文)所需要的区别。如果你的语言包括一个atan2函数,请使用以下方法:

angleInDegrees = atan2(deltaY, deltaX) * 180 / PI

EDIT (Feb. 22, 2017): In general, however, calling atan2(deltaY,deltaX) just to get the proper angle for cos and sin may be inelegant. In those cases, you can often do the following instead:

编辑(2017年2月22日):一般来说,打电话给atan2(deltaY,deltaX)只是为了得到合适的角度,因为cos和sin可能不雅。在这些情况下,你可以经常这样做:

  1. Treat (deltaX, deltaY) as a vector.
  2. 把(deltaX, deltaY)当作一个矢量。
  3. Normalize that vector to a unit vector. To do so, divide deltaX and deltaY by the vector's length (sqrt(deltaX*deltaX+deltaY*deltaY)), unless the length is 0.
  4. 将这个向量标准化到一个单位向量。为了做到这一点,将deltaX和deltaY除以向量的长度(sqrt(deltaX*deltaX+deltaY*deltaY)),除非长度为0。
  5. After that, deltaX will now be the cosine of the angle between the vector and the horizontal axis (in the direction from the positive X to the positive Y axis at P1).
  6. 在此之后,deltaX将是矢量和水平轴之间夹角的余弦(从正X方向到P1的正Y轴)。
  7. And deltaY will now be the sine of that angle.
  8. 而deltaY就是这个角的正弦值。
  9. If the vector's length is 0, it won't have an angle between it and the horizontal axis (so it won't have a meaningful sine and cosine).
  10. 如果向量的长度是0,它和横轴之间没有夹角(所以它不会有一个有意义的正弦和余弦)。

EDIT (Feb. 28, 2017): Even without normalizing (deltaX, deltaY):

编辑(2017年2月28日):即使不规范(deltaX, deltaY):

  • The sign of deltaX will tell you whether the cosine described in step 3 is positive or negative.
  • deltaX的符号将告诉你第3步中描述的cos是正的还是负的。
  • The sign of deltaY will tell you whether the sine described in step 4 is positive or negative.
  • deltaY的符号会告诉你第4步中描述的sin是正的还是负的。
  • The signs of deltaX and deltaY will tell you which quadrant the angle is in, in relation to the positive X axis at P1:
    • +deltaX, +deltaY: 0 to 90 degrees.
    • +deltaX +deltaY: 0到90度。
    • -deltaX, +deltaY: 90 to 180 degrees.
    • -deltaX, +deltaY: 90到180度。
    • -deltaX, -deltaY: 180 to 270 degrees (-180 to -90 degrees).
    • -deltaY: 180到270度(-180到-90度)。
    • +deltaX, -deltaY: 270 to 360 degrees (-90 to 0 degrees).
    • +deltaX, -deltaY: 270 - 360度(-90 - 0度)。
  • deltaX和deltaY的符号会告诉你哪个象限的角度,与P1的正X轴有关:+deltaX, +deltaY: 0到90度。-deltaX, +deltaY: 90到180度。-deltaY: 180到270度(-180到-90度)。+deltaX, -deltaY: 270 - 360度(-90 - 0度)。

An implementation in Python using radians (provided on July 19, 2015 by Eric Leschinski, who edited my answer):

使用radians的Python实现(由Eric Leschinski于2015年7月19日提供,他编辑了我的答案):

from math import *
def angle_trunc(a):
    while a < 0.0:
        a += pi * 2
    return a

def getAngleBetweenPoints(x_orig, y_orig, x_landmark, y_landmark):
    deltaY = y_landmark - y_orig
    deltaX = x_landmark - x_orig
    return angle_trunc(atan2(deltaY, deltaX))

angle = getAngleBetweenPoints(5, 2, 1,4)
assert angle >= 0, "angle must be >= 0"
angle = getAngleBetweenPoints(1, 1, 2, 1)
assert angle == 0, "expecting angle to be 0"
angle = getAngleBetweenPoints(2, 1, 1, 1)
assert abs(pi - angle) <= 0.01, "expecting angle to be pi, it is: " + str(angle)
angle = getAngleBetweenPoints(2, 1, 2, 3)
assert abs(angle - pi/2) <= 0.01, "expecting angle to be pi/2, it is: " + str(angle)
angle = getAngleBetweenPoints(2, 1, 2, 0)
assert abs(angle - (pi+pi/2)) <= 0.01, "expecting angle to be pi+pi/2, it is: " + str(angle)
angle = getAngleBetweenPoints(1, 1, 2, 2)
assert abs(angle - (pi/4)) <= 0.01, "expecting angle to be pi/4, it is: " + str(angle)
angle = getAngleBetweenPoints(-1, -1, -2, -2)
assert abs(angle - (pi+pi/4)) <= 0.01, "expecting angle to be pi+pi/4, it is: " + str(angle)
angle = getAngleBetweenPoints(-1, -1, -1, 2)
assert abs(angle - (pi/2)) <= 0.01, "expecting angle to be pi/2, it is: " + str(angle)

All tests pass. See https://en.wikipedia.org/wiki/Unit_circle

所有测试通过。参见https://en.wikipedia.org/wiki/Unit_circle

#2


47  

Sorry, but I'm pretty sure Peter's answer is wrong. Note that the y axis goes down the page (common in graphics). As such the deltaY calculation has to be reversed, or you get the wrong answer.

对不起,我很确定彼得的答案是错的。注意,y轴向下(在图形中常见)。因此,deltaY的计算必须被逆转,否则你会得到错误的答案。

Consider:

考虑:

System.out.println (Math.toDegrees(Math.atan2(1,1)));
System.out.println (Math.toDegrees(Math.atan2(-1,1)));
System.out.println (Math.toDegrees(Math.atan2(1,-1)));
System.out.println (Math.toDegrees(Math.atan2(-1,-1)));

gives

给了

45.0
-45.0
135.0
-135.0

So if in the example above, P1 is (1,1) and P2 is (2,2) [because Y increases down the page], the code above will give 45.0 degrees for the example shown, which is wrong. Change the order of the deltaY calculation and it works properly.

因此,如果在上面的例子中,P1(1,1)和P2(2,2)(因为Y增加了页面),上面的代码将给出示例显示的45.0度,这是错误的。更改deltaY计算的顺序,它可以正常工作。

#3


1  

I have found a solution in Python that is working well !

我已经在Python中找到了一个运行良好的解决方案!

from math import atan2,degrees

def GetAngleOfLineBetweenTwoPoints(p1, p2):
    return degrees(atan2(p2 - p1, 1))

print GetAngleOfLineBetweenTwoPoints(1,3)

#4


1  

Considering the exact question, putting us in a "special" coordinates system where positive axis means moving DOWN (like a screen or an interface view), you need to adapt this function like this, and negative the Y coordinates:

考虑到确切的问题,把我们放在一个“特殊的”坐标系中,正轴意味着向下移动(像屏幕或界面视图),你需要调整这个函数,像这样,负的Y坐标:

Example in Swift 2.0

例子在斯威夫特2.0

func angle_between_two_points(pa:CGPoint,pb:CGPoint)->Double{
    let deltaY:Double = (Double(-pb.y) - Double(-pa.y))
    let deltaX:Double = (Double(pb.x) - Double(pa.x))
    var a = atan2(deltaY,deltaX)
    while a < 0.0 {
        a = a + M_PI*2
    }
    return a
}

This function gives a correct answer to the question. Answer is in radians, so the usage, to view angles in degrees, is:

这个函数给出了这个问题的正确答案。答案是在弧度内,所以用角度来观察角度,是:

let p1 = CGPoint(x: 1.5, y: 2) //estimated coords of p1 in question
let p2 = CGPoint(x: 2, y : 3) //estimated coords of p2 in question

print(angle_between_two_points(p1, pb: p2) / (M_PI/180))
//returns 296.56

#5


0  

Based on reference "Peter O".. Here is the java version

参考“彼得O”。这是java版本。

private static final float angleBetweenPoints(PointF a, PointF b) {
float deltaY = b.y - a.y;
float deltaX = b.x - a.x;
return (float) (Math.atan2(deltaY, deltaX)); }

#6


0  

deltaY = Math.Abs(P2.y - P1.y);
deltaX = Math.Abs(P2.x - P1.x);

angleInDegrees = Math.atan2(deltaY, deltaX) * 180 / PI

if(p2.y > p1.y) // Second point is lower than first, angle goes down (180-360)
{
  if(p2.x < p1.x)//Second point is to the left of first (180-270)
    angleInDegrees += 180;
  else (270-360)
    angleInDegrees += 270;
}
else if (p2.x < p1.x) //Second point is top left of first (90-180)
  angleInDegrees += 90;

#7


0  

matlab function:

matlab函数:

function [lineAngle] = getLineAngle(x1, y1, x2, y2) 
    deltaY = y2 - y1;
    deltaX = x2 - x1;

    lineAngle = rad2deg(atan2(deltaY, deltaX));

    if deltaY < 0
        lineAngle = lineAngle + 360;
    end
end