在 matplotlib 中以一定角度绘制直角三角形时遇到问题
Trouble plotting a right triangle at an angle in matplotlib
我正在尝试生成一个斜边 = 1、内角 25、底角旋转 30 度的直角三角形。根据我的理解,我已经正确输入了三角公式,但我怀疑存在某种舍入错误。因为在 matplot 中生成的三角形与直角三角形略有不同。
import math
import matplotlib.pyplot as plt
from annotation import label
angle_b = math.radians(25) # the interior 25 degree angle of our right triangle
angle_a = math.radians(30) # 30 degree angle between the plotted angle_b triangle base and the x axis.
point_A = (0,0)
point_B = (math.cos(angle_a + angle_b), math.sin(angle_a + angle_b))
point_C = (math.cos(angle_a) * math.cos(angle_b), math.sin(angle_a) * math.cos(angle_b))
# Label our points
label(plt, 'A', point_A)
label(plt, 'B', point_B)
label(plt, 'C', point_C)
# Draw the right triangle between our points.
plt.plot(*zip(point_A, point_B, point_C, point_A), marker='o', color='black')
如您所见,角度 ACB 不是直角 trigonometry would predict。
@Mad Physicist 指出了 ABC 确实看起来是直角三角形的评论。我想让 ACB 成为一个直角,比如 this example.
下面是angle_a形成的直角三角形:
point_D = (math.cos(angle_a) * math.cos(angle_b), 0)
plt.plot(*zip(point_A, point_C, point_D, point_A), marker='o', color='black')
label(plt, 'D', point_D)
现已解决。由于 ax.set_aspect('equal')
行,我终于让它工作了
你好像搞错了三角函数。我将建议逐步缓慢移动,并使用可帮助您记住正在发生的事情的变量名称。
让我们从原来的三角形开始。如果interior_angle = np.deg2rad(25)
且斜边的长度为1
,则x轴上的直角在(np.cos(interior_angle), 0)
处,另一个内角在(np.cos(interior_angle), np.sin(interior_angle))
处。你图表中的两个点都不对应。
现在让我们将三角形表示为以顶点为列的矩阵:
interior_angle = np.deg2rad(25)
vertices = np.array([
[0, np.cos(interior_angle), np.cos(interior_angle), 0],
[0, 0, np.sin(interior_angle), 0],
])
最后一个顶点是原点的重复,使绘图更容易。
现在让我们看看旋转。对于 rotation_angle = np.deg2rad(30)
,点 (x, y)
旋转到 (np.cos(rotation_angle) * x - np.sin(rotation_angle) * y, np.sin(rotation_angle) * x + np.cos(rotation_angle) * y)
。这可以表示为矩阵方程:
rotation_matrix = np.array([
[np.cos(rotation_angle), -np.sin(rotation_angle)],
[np.sin(rotation_angle), np.cos(rotation_angle)]])
p_out = rotation_matrix @ p_in
数组vertices
是这样构造的,可以直接乘以一个旋转矩阵。因此你可以写
rotation_angle = np.deg2rad(30)
rotation_matrix = np.array([
[np.cos(rotation_angle), -np.sin(rotation_angle)],
[np.sin(rotation_angle), np.cos(rotation_angle)]])
rotated_vertices = rotation_matrix @ vertices
绘制的图像现在应该更有意义了:
plt.plot(*vertices)
plt.plot(*rotated_vertices)
plt.axis('equal')
plt.show()
问题是默认情况下,matplotlib 在 x 和 y 方向上不使用相同的距离。相反,matplotlib 会尝试将所有内容很好地放入给定范围内。
这些不均匀的距离会扭曲角度,也会使圆变形。
您可以通过 ax.set_aspect('equal')
.
强制设置相同的宽高比
要通过角度计算位置,并在 B 点有右角,您需要考虑长度 AC 是 AC 长度的 cos(b)
倍。可以选择 AC 的长度为 1。或者,您可以将 B 和 C 除以 cos(b)
以获得更大的三角形,其中 AB 的长度为 1.
import matplotlib.pyplot as plt
import math
angle_b = math.radians(25) # the interior 25 degree angle of our right triangle
angle_a = math.radians(30) # 30 degree angle between the plotted angle_b triangle and the x axis.
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(14, 5), sharey=True)
for ax in (ax1, ax2):
point_A = (0, 0)
if ax == ax1:
point_B = (math.cos(angle_a + angle_b) * math.cos(angle_b), math.sin(angle_a + angle_b) * math.cos(angle_b))
point_C = (math.cos(angle_a), math.sin(angle_a))
ax.set_title('length AC is 1')
else:
point_B = (math.cos(angle_a + angle_b), math.sin(angle_a + angle_b))
point_C = (math.cos(angle_a) / math.cos(angle_b), math.sin(angle_a) / math.cos(angle_b))
ax.set_title('length AB is 1')
point_M = ((point_A[0] + point_C[0]) / 2, (point_A[1] + point_C[1]) / 2)
# Draw the right triangle between our points.
ax.plot(*zip(point_A, point_B, point_C, point_A), marker='o', color='black')
# draw a circle around the 3 points
ax.add_patch(plt.Circle(point_M, math.sqrt((point_M[0] - point_A[0]) ** 2 + (point_M[1] - point_A[1]) ** 2),
ec='r', ls='--', fc='none'))
ax.set_aspect('equal', 'datalim')
plt.show()
同样的计算适用于任何角度。这是以 30 度为步长旋转 12 次的效果:
下面的代码展示了ax.set_aspect('equal')
对原始点的影响
import matplotlib.pyplot as plt
import math
angle_b = math.radians(25) # the interior 25 degree angle of our right triangle
angle_a = math.radians(30) # 30 degree angle between the plotted angle_b triangle and the x axis.
point_A = (0, 0)
point_B = (math.cos(angle_a + angle_b), math.sin(angle_a + angle_b))
point_C = (math.cos(angle_a) * math.cos(angle_b), math.sin(angle_a) * math.cos(angle_b))
point_M = ((point_A[0] + point_B[0]) / 2, (point_A[1] + point_B[1]) / 2)
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(16, 4), gridspec_kw={'width_ratios': [2, 1]})
for ax in (ax1, ax2):
# Draw the right triangle between our points.
ax.plot(*zip(point_A, point_B, point_C, point_A), marker='o', color='black')
# draw a circle around the 3 points
ax.add_patch(plt.Circle(point_M, math.sqrt((point_M[0] - point_A[0]) ** 2 + (point_M[1] - point_A[1]) ** 2),
ec='r', ls='--', fc='none'))
ax1.set_title('Default aspect ratio, deforms the angles')
ax2.set_aspect('equal') # or plt.axis('equal')
ax2.set_title('Equal aspect ratio')
plt.tight_layout()
plt.show()
我正在尝试生成一个斜边 = 1、内角 25、底角旋转 30 度的直角三角形。根据我的理解,我已经正确输入了三角公式,但我怀疑存在某种舍入错误。因为在 matplot 中生成的三角形与直角三角形略有不同。
import math
import matplotlib.pyplot as plt
from annotation import label
angle_b = math.radians(25) # the interior 25 degree angle of our right triangle
angle_a = math.radians(30) # 30 degree angle between the plotted angle_b triangle base and the x axis.
point_A = (0,0)
point_B = (math.cos(angle_a + angle_b), math.sin(angle_a + angle_b))
point_C = (math.cos(angle_a) * math.cos(angle_b), math.sin(angle_a) * math.cos(angle_b))
# Label our points
label(plt, 'A', point_A)
label(plt, 'B', point_B)
label(plt, 'C', point_C)
# Draw the right triangle between our points.
plt.plot(*zip(point_A, point_B, point_C, point_A), marker='o', color='black')
如您所见,角度 ACB 不是直角 trigonometry would predict。
@Mad Physicist 指出了 ABC 确实看起来是直角三角形的评论。我想让 ACB 成为一个直角,比如 this example.
下面是angle_a形成的直角三角形:
point_D = (math.cos(angle_a) * math.cos(angle_b), 0)
plt.plot(*zip(point_A, point_C, point_D, point_A), marker='o', color='black')
label(plt, 'D', point_D)
现已解决。由于 ax.set_aspect('equal')
你好像搞错了三角函数。我将建议逐步缓慢移动,并使用可帮助您记住正在发生的事情的变量名称。
让我们从原来的三角形开始。如果interior_angle = np.deg2rad(25)
且斜边的长度为1
,则x轴上的直角在(np.cos(interior_angle), 0)
处,另一个内角在(np.cos(interior_angle), np.sin(interior_angle))
处。你图表中的两个点都不对应。
现在让我们将三角形表示为以顶点为列的矩阵:
interior_angle = np.deg2rad(25)
vertices = np.array([
[0, np.cos(interior_angle), np.cos(interior_angle), 0],
[0, 0, np.sin(interior_angle), 0],
])
最后一个顶点是原点的重复,使绘图更容易。
现在让我们看看旋转。对于 rotation_angle = np.deg2rad(30)
,点 (x, y)
旋转到 (np.cos(rotation_angle) * x - np.sin(rotation_angle) * y, np.sin(rotation_angle) * x + np.cos(rotation_angle) * y)
。这可以表示为矩阵方程:
rotation_matrix = np.array([
[np.cos(rotation_angle), -np.sin(rotation_angle)],
[np.sin(rotation_angle), np.cos(rotation_angle)]])
p_out = rotation_matrix @ p_in
数组vertices
是这样构造的,可以直接乘以一个旋转矩阵。因此你可以写
rotation_angle = np.deg2rad(30)
rotation_matrix = np.array([
[np.cos(rotation_angle), -np.sin(rotation_angle)],
[np.sin(rotation_angle), np.cos(rotation_angle)]])
rotated_vertices = rotation_matrix @ vertices
绘制的图像现在应该更有意义了:
plt.plot(*vertices)
plt.plot(*rotated_vertices)
plt.axis('equal')
plt.show()
问题是默认情况下,matplotlib 在 x 和 y 方向上不使用相同的距离。相反,matplotlib 会尝试将所有内容很好地放入给定范围内。
这些不均匀的距离会扭曲角度,也会使圆变形。
您可以通过 ax.set_aspect('equal')
.
要通过角度计算位置,并在 B 点有右角,您需要考虑长度 AC 是 AC 长度的 cos(b)
倍。可以选择 AC 的长度为 1。或者,您可以将 B 和 C 除以 cos(b)
以获得更大的三角形,其中 AB 的长度为 1.
import matplotlib.pyplot as plt
import math
angle_b = math.radians(25) # the interior 25 degree angle of our right triangle
angle_a = math.radians(30) # 30 degree angle between the plotted angle_b triangle and the x axis.
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(14, 5), sharey=True)
for ax in (ax1, ax2):
point_A = (0, 0)
if ax == ax1:
point_B = (math.cos(angle_a + angle_b) * math.cos(angle_b), math.sin(angle_a + angle_b) * math.cos(angle_b))
point_C = (math.cos(angle_a), math.sin(angle_a))
ax.set_title('length AC is 1')
else:
point_B = (math.cos(angle_a + angle_b), math.sin(angle_a + angle_b))
point_C = (math.cos(angle_a) / math.cos(angle_b), math.sin(angle_a) / math.cos(angle_b))
ax.set_title('length AB is 1')
point_M = ((point_A[0] + point_C[0]) / 2, (point_A[1] + point_C[1]) / 2)
# Draw the right triangle between our points.
ax.plot(*zip(point_A, point_B, point_C, point_A), marker='o', color='black')
# draw a circle around the 3 points
ax.add_patch(plt.Circle(point_M, math.sqrt((point_M[0] - point_A[0]) ** 2 + (point_M[1] - point_A[1]) ** 2),
ec='r', ls='--', fc='none'))
ax.set_aspect('equal', 'datalim')
plt.show()
同样的计算适用于任何角度。这是以 30 度为步长旋转 12 次的效果:
下面的代码展示了ax.set_aspect('equal')
对原始点的影响
import matplotlib.pyplot as plt
import math
angle_b = math.radians(25) # the interior 25 degree angle of our right triangle
angle_a = math.radians(30) # 30 degree angle between the plotted angle_b triangle and the x axis.
point_A = (0, 0)
point_B = (math.cos(angle_a + angle_b), math.sin(angle_a + angle_b))
point_C = (math.cos(angle_a) * math.cos(angle_b), math.sin(angle_a) * math.cos(angle_b))
point_M = ((point_A[0] + point_B[0]) / 2, (point_A[1] + point_B[1]) / 2)
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(16, 4), gridspec_kw={'width_ratios': [2, 1]})
for ax in (ax1, ax2):
# Draw the right triangle between our points.
ax.plot(*zip(point_A, point_B, point_C, point_A), marker='o', color='black')
# draw a circle around the 3 points
ax.add_patch(plt.Circle(point_M, math.sqrt((point_M[0] - point_A[0]) ** 2 + (point_M[1] - point_A[1]) ** 2),
ec='r', ls='--', fc='none'))
ax1.set_title('Default aspect ratio, deforms the angles')
ax2.set_aspect('equal') # or plt.axis('equal')
ax2.set_title('Equal aspect ratio')
plt.tight_layout()
plt.show()