我有一个算法,需要计算出图中边之间的有符号角度(-180 到 180)。我做了一些研究并找到了很多具体的答案,但无法弄清楚如何将它们与我的情况联系起来(例如 this 使用 atan2
的问题,但是OP只想要正角度)
我尝试过实现几种不同的方法(使用atan2或arccos),但我很难将这些示例与我的具体问题联系起来。我尝试将边缘视为向量,但得到了奇怪的结果。
给定一个包含点(A、B、C、D、E)的图表,以及这些点的平均值(avg)...我如何找到其中一个点(例如 A)和其他点(例如 B、C、D、E),将当前原点 (A) 到“avg”点的角度设为 0 度。下面的例子...
...在此示例中,从 (A, avg) 到 (A, B) 的逆时针角度将为正数(0 到 180 之间),从 (A, avg) 到 (A , E) 将为负数(0 到 -180 之间)。
理想情况下,我想要一个公式,我也可以将其应用于定义任何点作为原点,例如以点 C 作为原点。“零角度”将是 (C, avg) 和 ( C,avg)和(C,A)将为负(0到-180),(C,avg)和(C,E)之间的角度将为正(0到180)。
我高中毕业后就没有学过数学,所以我发现很难用我不理解的符号来破译方程式。
更新:我想我会清理这个以使结论更加明显。 我对已接受的答案做了两个小更改,得到了以下代码片段:
def angle(vertex, start, dest):
AhAB = math.atan2((dest.y - vertex.y), (dest.x - vertex.x))
AhAO = math.atan2((start.y - vertex.y), (start.x - vertex.x))
AB = AhAB - AhAO
# in between 0-math.pi = do nothing, more than math.pi = +(-2 * math.pi), less than zero = do nothing
AB = math.degrees(AB + (-2 * math.pi if AB > math.pi else (2 * math.pi if AB < 0 - math.pi else 0)))
return AB
...在几个月没有处理这个问题之后,最后的一句台词可能有点难以理解,所以我把它变成了它自己的函数,得到 AB = AhAB - AhAO
的结果。正如它的论点...
def calc(ab):
if ab > math.pi:
return ab + (-2 * math.pi)
else:
if ab < 0 - math.pi:
return ab + (2 * math.pi)
else:
return ab + 0
我认为这更容易阅读,尽管行数更多。
完整的最终功能:
def angle(vertex, start, dest):
"""Calculates the signed angle between two edges with the same origin.
Origin is the 'vertex' argument, 'start' is the bounding point of the edge to calculate the angle from.
Positively signed result means anti-clockwise rotation about the vertex."""
def calc_radians(ab):
if ab > math.pi:
return ab + (-2 * math.pi)
else:
if ab < 0 - math.pi:
return ab + (2 * math.pi)
else:
return ab + 0
AhAB = math.atan2((dest.y - vertex.y), (dest.x - vertex.x))
AhAO = math.atan2((start.y - vertex.y), (start.x - vertex.x))
res = calc_radians(AhAB - AhAO)
return math.degrees(res)
注意:该函数假设三个参数都是典型 Point
的实例。与 x
一起上课和y
属性。
另外,上面的示例图只有正值,但我相当确定这也适用于涉及负值的图。
最佳答案
我读了你的问题陈述如下:给定 2 个点 A 和 B,以及一个中心 O,找到角度 A 到 B 作为向量 A→O 和 A→B 之间的角度,如果逆时针,则为正。
如果我的前提是正确的,那么你就可以
- 求 A→B 与穿过 A 的水平向右线之间的角度,
- 求 A→O 与穿过 A 的水平向右线之间的角度,
- 求 A 到 B 的角度作为所述角度的差,
- 标准化结果范围,使其介于 -π 和 +π 之间。
我所说的可以形象化如下
AhAB = math.atan2((B.y-A.y), (B.x-A.x)) # -π < AhAB ≤ +π
AhAO = math.atan2((O.y-A.y), (O.x-A.x)) # -π < AhA) ≤ +π
AB = AhAB - AhAO # -2π < AB ≤ +2π
AB = AB + ( 2*math.pi if AB < math.pi else (-2*math.pi if AB> math.pi else 0))
附录
这是一个小代码示例,点的位置与您在图片中看到的相似
In [18]: from math import atan2, pi
In [21]: class Point():
...: def __init__(self, x, y):
...: self.x, self.y = x, y
...: def __repr__(self):
...: return '(%s, %s)'%(self.x, self.y)
In [22]: A = Point(0.0, 0.0)
In [23]: B = Point(-2.0, 2.0)
In [24]: O = Point(0.0, -3.0)
In [25]: AhAB = atan2((B.y-A.y), (B.x-A.x)) ; print(3/4, AhAB/pi)
0.75 0.75
In [26]: AhAO = atan2((O.y-A.y), (O.x-A.x)) ; print(-1/2, AhAO/pi)
-0.5 -0.5
In [27]: AB = AhAB - AhAO ; print(5/4, AB/pi)
1.25 1.25
In [28]: AB = AB + ( 2*pi if AB < pi else (-2*pi if AB> pi else 0)) ; print(AB/pi)
-0.75
In [29]:
最后一行标准化你的结果 AB
在正确的范围内-π < AB ≤ π
,加或减2π
这不会改变测量角度的含义。
关于python - 有向线段之间的符号角,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52571812/