关于c#:如何计算直线与水平轴之间的角度?

How to calculate the angle between a line and the horizontal axis?

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

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

no words can describe this

给定(p1x,p1y)和(p2x,p2y)计算此角度的最佳方法是什么?原点在左上角,仅使用正象限。


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

1
2
deltaY = P2_y - P1_y
deltaX = P2_x - P1_x

然后计算角度(从EDOCX1的正x轴(0)到EDOCX1的正y轴(0))。

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

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

2

编辑(2017年2月22日):但是,一般来说,打电话给atan2(deltaY,deltaX),只是为了获得cossin的合适角度,可能不太好。在这些情况下,您通常可以执行以下操作:

  • (deltaX, deltaY)作为载体。
  • 将该向量规范化为单位向量。为此,将deltaXdeltaY除以向量的长度(sqrt(deltaX*deltaX+deltaY*deltaY)),除非长度为0。
  • 之后,deltaX将是向量与水平轴之间的角度的余弦(在P1处从正x轴到正y轴的方向)。
  • 现在,deltaY就是这个角度的正弦值。
  • 如果向量的长度为0,那么它与水平轴之间就没有角度(因此它没有有意义的正弦和余弦)。
  • 编辑(2017年2月28日):即使没有规范化(deltaX, deltaY)

    • deltaX的符号将告诉您步骤3中描述的余弦是正的还是负的。
    • deltaY的符号将告诉您步骤4中描述的正弦是正的还是负的。
    • deltaXdeltaY的符号将告诉您,相对于P1处的正x轴,角度在哪个象限内:
      • +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日提供,他编辑了我的答案):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    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轴沿页面向下移动(在图形中很常见)。因此,Deltay的计算必须颠倒,否则你会得到错误的答案。

    考虑:

    1
    2
    3
    4
    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)));

    给予

    1
    2
    3
    4
    45.0
    -45.0
    135.0
    -135.0

    因此,如果在上面的例子中,p1是(1,1),p2是(2,2)[因为y在页面下方增加],上面的代码将给出所示例子的45.0度,这是错误的。更改Deltay计算的顺序,它可以正常工作。


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

    Swift 2.0中的示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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
    }

    此函数给出了问题的正确答案。答案是以弧度表示的,所以用度数表示角度的用法是:

    1
    2
    3
    4
    5
    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

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

    1
    2
    3
    4
    5
    6
    from math import atan2,degrees

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

    print GetAngleOfLineBetweenTwoPoints(1,3)

    从0到2pi的角度的公式。

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

    x和y的任何值。对于x=y=0,结果未定义。

    f(x,y)=pi()-pi()/2*(1+符号(x))*(1-符号(y^2))

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

         -sign(x*y)*atan((abs(x)-abs(y))/(abs(x)+abs(y)))

    matlab函数:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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;

    基于参考文献"Peter O"…这里是Java版本

    1
    2
    3
    4
    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)); }