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

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

我认为图片最能描述我想要的东西:

没有文字可以描述这

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

241052 次浏览

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

deltaY = P2_y - P1_y
deltaX = P2_x - P1_x

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

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

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

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

编辑(2017年2月22日):然而,一般来说,调用atan2(deltaY,deltaX)只是为了获得cossin的适当角度可能是不优雅的。在这些情况下,你通常可以做以下事情:

  1. (deltaX, deltaY)作为一个向量。
  2. 将这个向量归一化为单位向量。要做到这一点,用deltaXdeltaY除以向量的长度(sqrt(deltaX*deltaX+deltaY*deltaY)),除非长度为0。
  3. 在此之后,deltaX现在将是向量与横轴之间夹角的余弦(在P1处从正X到正Y轴的方向上)。
  4. deltaY现在就是这个角的正弦。
  5. 如果向量的长度为0,那么它与横轴之间就没有夹角(因此它没有有意义的正弦和余弦)。

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

  • deltaX的符号将告诉你步骤3中描述的余弦是正的还是负的。
  • deltaY的符号将告诉你步骤4中描述的sin是正的还是负的。
  • deltaXdeltaY的符号将告诉你这个角在哪个象限,相对于P1的正X轴:
    • +deltaX+deltaY: 0到90度。
    • -deltaX+deltaY: 90到180度。
    • -deltaX-deltaY: 180到270度(-180到-90度)。
    • +deltaX-deltaY: 270到360度(-90到0度)。
    • 李< / ul > < / >

    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)
    

    所有测试都通过。看到https://en.wikipedia.org/wiki/Unit_circle

抱歉,但我很确定彼得的答案是错的。注意y轴是向下的(在图形中很常见)。因此,延迟计算必须反转,否则就会得到错误的答案。

考虑:

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

给了

45.0
-45.0
135.0
-135.0

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

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

from math import atan2,degrees


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


print GetAngleOfLineBetweenTwoPoints(1,3)

根据参考“彼得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)); }

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

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

这个函数给出了问题的正确答案。答案是以弧度为单位,所以用角度来表示角度的用法是:

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

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

一个从0到2的角的公式。

有x=x2-x1 y=y2-y1。这个公式适用于

x和y的任意值。对于x=y=0,结果没有定义。

f (x, y) =π-π()()/ 2 *(1 +签署(x)) * (1-sign (y ^ 2))

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


-sign(x*y)*atan((abs(x)-abs(y))/(abs(x)+abs(y)))
import math
from collections import namedtuple




Point = namedtuple("Point", ["x", "y"])




def get_angle(p1: Point, p2: Point) -> float:
"""Get the angle of this line with the horizontal axis."""
dx = p2.x - p1.x
dy = p2.y - p1.y
theta = math.atan2(dy, dx)
angle = math.degrees(theta)  # angle is in (-180, 180]
if angle < 0:
angle = 360 + angle
return angle

测试

为了测试,我让假设生成测试用例。

enter image description here

import hypothesis.strategies as s
from hypothesis import given




@given(s.floats(min_value=0.0, max_value=360.0))
def test_angle(angle: float):
epsilon = 0.0001
x = math.cos(math.radians(angle))
y = math.sin(math.radians(angle))
p1 = Point(0, 0)
p2 = Point(x, y)
assert abs(get_angle(p1, p2) - angle) < epsilon