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

2025-02-10 08:57:00
admin
原创
53
摘要:问题描述:在编程语言(Python、C# 等)中,我需要确定如何计算线和水平轴之间的角度?我认为一张图片最能描述我想要的东西:给定 (P1 x ,P1 y ) 和 (P2 x ,P2 y ),计算该角度的最佳方法是什么?原点位于左上角,仅使用正象限。解决方案 1:首先找到起点和终点之间的差异(这里,这更像是一...

问题描述:

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

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

没有词语可以形容这一点

给定 (P1 x ,P1 y ) 和 (P2 x ,P2 y ),计算该角度的最佳方法是什么?原点位于左上角,仅使用正象限。


解决方案 1:

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

deltaY = P2_y - P1_y
deltaX = P2_x - P1_x

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

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

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

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

编辑(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 中描述的正弦是正值还是负值。

  • deltaX和的符号deltaY表示该角位于相对于 处的正 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)

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

解决方案 2:

抱歉,但我确信 Peter 的答案是错误的。请注意,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)));

给出

45.0
-45.0
135.0
-135.0

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

解决方案 3:

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

测试

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

在此处输入图片描述

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

解决方案 4:

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

from math import atan2,degrees

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

print GetAngleOfLineBetweenTwoPoints(1,3)

解决方案 5:

根据参考“Peter 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:

考虑到确切的问题,将我们置于一个“特殊”的坐标系中,其中正轴表示向下移动(如屏幕或界面视图),您需要像这样调整此功能,并将 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

解决方案 7:

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

解决方案 8:

从 0 到 2pi 的角度公式。

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

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

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

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

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

解决方案 9:

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;
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用