使用math.atan2计算线段(Python)之间的角度

Ale*_*kov 6 python trigonometry angle atan2 cartesian-coordinates

我正在研究空间分析问题,该工作流程的一部分是计算连接线段之间的角度.

每个线段仅由两个点组成,每个点都有一对XY坐标(笛卡尔坐标).这是GeoGebra的图像.我总是对在0到180范围内获得正角度感兴趣.但是,根据输入线段中顶点的顺序,我得到所有类型的角度.

在此输入图像描述

我使用的输入数据以坐标元组的形式提供.根据顶点创建顺序,每个线段的最后/结束点可以不同.以下是Python代码中的一些案例.我得到它们的线段的顺序是随机的,但在元组的元组中,第一个元素是起点,第二个元素是终点.DE线段,例如,本来((1,1.5),(2,2))(1,1.5)为起点,因为它在坐标元组第一的位置.

但是,我需要确保我会得到的相同的角度DE,DF,并ED,DF依此类推.

vertexType = "same start point; order 1"
            #X, Y    X Y coords
lineA = ((1,1.5),(2,2)) #DE
lineB = ((1,1.5),(2.5,0.5)) #DF
calcAngle(lineA, lineB,vertexType)
#flip lines order
vertexType = "same start point; order 2"
lineB = ((1,1.5),(2,2)) #DE
lineA = ((1,1.5),(2.5,0.5)) #DF
calcAngle(lineA, lineB,vertexType)

vertexType = "same end point; order 1"
lineA = ((2,2),(1,1.5)) #ED
lineB = ((2.5,0.5),(1,1.5)) #FE
calcAngle(lineA, lineB,vertexType)
#flip lines order
vertexType = "same end point; order 2"
lineB = ((2,2),(1,1.5)) #ED
lineA = ((2.5,0.5),(1,1.5)) #FE
calcAngle(lineA, lineB,vertexType)

vertexType = "one line after another - down; order 1"
lineA = ((2,2),(1,1.5)) #ED
lineB = ((1,1.5),(2.5,0.5)) #DF
calcAngle(lineA, lineB,vertexType)
#flip lines order
vertexType = "one line after another - down; order 2"
lineB = ((2,2),(1,1.5)) #ED
lineA = ((1,1.5),(2.5,0.5)) #DF
calcAngle(lineA, lineB,vertexType)

vertexType = "one line after another - up; line order 1"
lineA = ((1,1.5),(2,2)) #DE
lineB = ((2.5,0.5),(1,1.5)) #FD
calcAngle(lineA, lineB,vertexType)
#flip lines order
vertexType = "one line after another - up; line order 2"
lineB = ((1,1.5),(2,2)) #DE
lineA = ((2.5,0.5),(1,1.5)) #FD
calcAngle(lineA, lineB,vertexType)
Run Code Online (Sandbox Code Playgroud)

我写了一个很小的函数,它将行的组合作为args并计算它们之间的角度.我使用的math.atan2似乎最适合这个.

def calcAngle(lineA,lineB,vertexType):
    line1Y1 = lineA[0][1]
    line1X1 = lineA[0][0]
    line1Y2 = lineA[1][1]
    line1X2 = lineA[1][0]

    line2Y1 = lineB[0][1]
    line2X1 = lineB[0][0]
    line2Y2 = lineB[1][1]
    line2X2 = lineB[1][0]

    #calculate angle between pairs of lines
    angle1 = math.atan2(line1Y1-line1Y2,line1X1-line1X2)
    angle2 = math.atan2(line2Y1-line2Y2,line2X1-line2X2)
    angleDegrees = (angle1-angle2) * 360 / (2*math.pi)
    print angleDegrees, vertexType
Run Code Online (Sandbox Code Playgroud)

我得到的输出是:

> -299.744881297 same start point; order 1
> 299.744881297 same start point; order 2
> 60.2551187031 same end point; order 1
> -60.2551187031 same end point; order 2
> -119.744881297 one line after another - down; order 1
> 119.744881297 one line after another - down; order 2
> -119.744881297 one line after another - up; line order 1
> 119.744881297 one line after another - up; line order 2
Run Code Online (Sandbox Code Playgroud)

如您所见,我将获得不同的值,具体取决于线段和线段顺序中的顶点顺序.我试图通过找出源线有什么样的关系和翻转线,编辑角度等来对角度进行后处理.我已经结束了十几个这样的情况,并且在某些时候它们开始重叠而且我不能再发现-119.744是否应该变成60.255(锐角)或者是119.744(钝角)等.

有没有任何离散的方法来处理我收到的输出角度值,math.atan2只能得到0到180范围内的正值? 如果没有,我应该采取什么样的其他方法?

Ign*_*ams 6

太多工作。取两个向量的点积除以每条线的长度的反余弦的绝对值。


Abh*_*nan 6

解决这个问题的最简单和最逻辑的方法是使用点积.

试试这个代码(我几乎评论过所有内容):

import math
def dot(vA, vB):
    return vA[0]*vB[0]+vA[1]*vB[1]
def ang(lineA, lineB):
    # Get nicer vector form
    vA = [(lineA[0][0]-lineA[1][0]), (lineA[0][1]-lineA[1][1])]
    vB = [(lineB[0][0]-lineB[1][0]), (lineB[0][1]-lineB[1][1])]
    # Get dot prod
    dot_prod = dot(vA, vB)
    # Get magnitudes
    magA = dot(vA, vA)**0.5
    magB = dot(vB, vB)**0.5
    # Get cosine value
    cos_ = dot_prod/magA/magB
    # Get angle in radians and then convert to degrees
    angle = math.acos(dot_prod/magB/magA)
    # Basically doing angle <- angle mod 360
    ang_deg = math.degrees(angle)%360

    if ang_deg-180>=0:
        # As in if statement
        return 360 - ang_deg
    else: 

        return ang_deg
Run Code Online (Sandbox Code Playgroud)

现在尝试你的lineA和lineB的变化,所有应该给出相同的答案.

  • '%360' 是多余的,因为 arccos (math.acos) 的输出位于 [0, pi] 弧度(或 [0, 180] 度)区间内。因此,角度永远不应小于 0 度或大于 180 度。后面的 if-else 语句也是多余的。您还应该考虑将 math.acos 的输入剪辑为 -1(最小值)和 1(最大值)。 (3认同)

小智 6

使用公式的替代解决方案:

在此处输入图片说明

其中“m1”是第 1 行的斜率,“m2”是第 2 行的斜率。如果第 1 行由点 P1 = [x1, y1] 和 P2 = [x2, y2] 定义,则斜率“m”是:

在此处输入图片说明

通过使用上面的公式,您可以找到两条线之间的角度,如下所示:

def slope(x1, y1, x2, y2): # Line slope given two points:
    return (y2-y1)/(x2-x1)

def angle(s1, s2): 
    return math.degrees(math.atan((s2-s1)/(1+(s2*s1))))

lineA = ((0.6, 3.6), (1.6, 3))
lineB = ((1.6, 3), (2, 3.6))

slope1 = slope(lineA[0][0], lineA[0][1], lineA[1][0], lineA[1][1])
slope2 = slope(lineB[0][0], lineB[0][1], lineB[1][0], lineB[1][1])

ang = angle(slope1, slope2)
print('Angle in degrees = ', ang)
Run Code Online (Sandbox Code Playgroud)