如何计算直线与水平轴之间的角度?

orl*_*rlp 246 c# python trigonometry

在编程语言(Python,C#等)中,我需要确定如何计算直线和水平轴之间的角度?

我认为图像描述的最符合我的要求:

没有言语可以形容这一点

给定(P1 x,P1 y)和(P2 x,P2 y)计算此角度的最佳方法是什么?原点在于topleft,只使用正象限.

Pet*_* O. 383

首先找到起点和终点之间的差异(这里,这更像是有向线段,而不是"线",因为线无限延伸而不是从特定点开始).

deltaY = P2_y - P1_y
deltaX = P2_x - P1_x
Run Code Online (Sandbox Code Playgroud)

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

angleInDegrees = arctan(deltaY / deltaX) * 180 / PI
Run Code Online (Sandbox Code Playgroud)

arctan可能并不理想,因为以这种方式划分差异将消除区分角度所在的象限所需的区别(见下文).如果您的语言包含以下atan2功能,请使用以下内容:

angleInDegrees = atan2(deltaY, deltaX) * 180 / PI
Run Code Online (Sandbox Code Playgroud)

编辑(2017年2月22日):然而,一般来说,atan2(deltaY,deltaX)只是为了获得适当的角度cos而且sin可能是不优雅的.在这些情况下,您通常可以执行以下操作:

  1. (deltaX, deltaY)作为矢量对待.
  2. 将该向量标准化为单位向量.为此,分频deltaXdeltaY由向量的长度(sqrt(deltaX*deltaX+deltaY*deltaY)),除非长度为0.
  3. 之后,deltaX现在将是矢量和水平轴之间角度的余弦(在从正X到正Y轴的方向上P1).
  4. deltaY现在将是角度的正弦值.
  5. 如果向量的长度为0,则它​​与水平轴之间不会有一个角度(因此它没有有意义的正弦和余弦).

编辑(2017年2月28日):即使没有正常化(deltaX, deltaY):

  • 标志deltaX将告诉您步骤3中描述的余弦是正还是负.
  • 该标志deltaY将告诉您步骤4中描述的正弦是正还是负.
  • 符号deltaXdeltaY将告诉您角度所在的象限,相对于正X轴P1:
    • +deltaX,+deltaY:0到90度.
    • -deltaX,+deltaY:90到180度.
    • -deltaX,-deltaY:180至270度(-180至-90度).
    • +deltaX,-deltaY:270至360度(-90至0度).

使用弧度的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)
Run Code Online (Sandbox Code Playgroud)

所有测试都通过.请参阅https://en.wikipedia.org/wiki/Unit_circle

  • 如果您发现了这个并且您正在使用JAVASCRiPT,那么请注意Math.sin和Math.cos采用弧度非常重要,因此您无需将结果转换为度数!所以忽略*180/PI位.我花了4个小时才找到它.:) (34认同)
  • @akashg:`90 - angleInDegrees`? (3认同)
  • @sidonaldson它不仅仅是Javascript,它是C,C#,C++,Java等.实际上我敢说大多数语言的数学库主要使用弧度.我还没有看到一种默认只支持度数的语言. (2认同)

小智 50

对不起,但我很确定彼得的回答是错的.请注意,y轴沿着页面向下(图形中常见).因此,deltaY计算必须反转,否则你会得到错误的答案.

考虑:

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)));
Run Code Online (Sandbox Code Playgroud)

45.0
-45.0
135.0
-135.0
Run Code Online (Sandbox Code Playgroud)

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

  • 如果能显示正确的方法会很好. (10认同)
  • 我按照你的建议改变它,我的轮换是倒退的. (3认同)