基于Matplotlib的Python外摆线动画:按尺寸适配帧范围
外摆线动画帧范围适配解决方案
问题描述
当设置ratio=2时,生成的epicycloid.gif动画无法完整显示整条外摆线,需要让动画的帧范围根据滚动小圆的半径(即ratio值)自动适配,确保完整绘制出整条曲线。
问题根源
- 外摆线的完整周期由大圆半径R与小圆半径r的比值
ratio = R/r决定:- 若ratio为整数k,外摆线有k个尖点,只需小圆绕大圆转1圈(φ从0到2π)即可完成整条曲线
- 若ratio为最简分数p/q,需小圆绕大圆转q圈(φ从0到2π*q)才能画出完整的p个尖点的外摆线
- 原代码使用固定的
ncycles参数,无法根据ratio自动调整动画周期,导致部分场景下曲线绘制不完整 - 未设置合适的坐标轴范围,可能导致曲线超出显示区域
修改后的代码
import matplotlib.pyplot as plt from matplotlib.animation import FuncAnimation import numpy as np import math class Epicycloid: def __init__(self, ratio, frames_per_cycle): self.frames_per_cycle = frames_per_cycle self.fig, self.ax = plt.subplots() self.ax.set_aspect('equal') # 大圆(固定) theta = np.linspace(0, 2 * np.pi, 100) x = np.cos(theta) y = np.sin(theta) self.big_circle, = self.ax.plot(x, y, 'b-') # 小圆参数 self.small_r = 1. / ratio r = self.small_r # 计算外摆线完整周期所需的转动圈数 # 将ratio转为最简分数,分母即为需要的圈数 numerator, denominator = self._simplify_ratio(ratio) self.full_cycles = denominator self.total_frames = self.frames_per_cycle * self.full_cycles # 设置坐标轴范围,确保完整显示外摆线 max_range = 1 + 2 * r self.ax.set_xlim(-max_range, max_range) self.ax.set_ylim(-max_range, max_range) # 初始小圆位置 x = r * np.cos(theta) + (1 + r) y = r * np.sin(theta) self.small_circle, = self.ax.plot(x, y, 'k-') # 连接线和跟踪点 self.line, = self.ax.plot([1 + r, 1], [0, 0], 'k-') self.dot, = self.ax.plot([1 + r], [0], 'go', ms=5) # 外摆线曲线 self.epicycloid, = self.ax.plot([], [], 'r-') # 创建动画 self.animation = FuncAnimation( self.fig, self.animate, frames=self.total_frames, interval=50, blit=False, repeat_delay=2000, ) def _simplify_ratio(self, ratio): # 将ratio转为最简分数,返回分子和分母 if isinstance(ratio, int): return ratio, 1 # 处理浮点数,转为分数 frac = math.fraction(ratio) return frac.numerator, frac.denominator def update_small_circle(self, phi): # 更新滚动小圆的位置 theta = np.linspace(0, 2 * np.pi, 100) x = self.small_r * np.cos(theta) + (1 + self.small_r) * np.cos(phi) y = self.small_r * np.sin(theta) + (1 + self.small_r) * np.sin(phi) self.small_circle.set_data(x, y) def update_epicycloid(self, phis): # 更新外摆线路径 R = 1 r = self.small_r # 外摆线标准参数方程(移除原代码中的+np.pi,避免相位偏移) x = (R + r) * np.cos(phis) - r * np.cos((R + r) / r * phis) y = (R + r) * np.sin(phis) - r * np.sin((R + r) / r * phis) self.epicycloid.set_data(x, y) # 更新连接线和跟踪点 last_phi = phis[-1] center = [(R + r) * np.cos(last_phi), (R + r) * np.sin(last_phi)] self.line.set_data([center[0], x[-1]], [center[1], y[-1]]) self.dot.set_data([x[-1]], [y[-1]]) def animate(self, frame): frame = frame + 1 # 计算当前相位,覆盖完整的周期 phi = 2 * np.pi * frame * self.full_cycles / self.total_frames self.update_small_circle(phi) self.update_epicycloid(np.linspace(0, phi, frame)) # 创建外摆线实例:ratio=2,每个基础周期50帧 epicycloid = Epicycloid(ratio=2, frames_per_cycle=50) # 保存为gif(需安装imagemagick) # epicycloid.animation.save('epicycloid.gif', writer='imagemagick', fps=10, dpi=75) plt.show()
关键修改说明
- 自动计算完整周期:新增
_simplify_ratio方法将ratio转为最简分数,根据分母确定小圆需要绕大圆转动的圈数,确保动画覆盖完整的外摆线绘制周期 - 动态调整总帧数:总帧数由
frames_per_cycle * full_cycles决定,保证每个完整曲线周期有足够的帧来平滑绘制 - 设置坐标轴范围:根据外摆线的最大尺寸(
1 + 2*small_r)设置xlim和ylim,避免曲线超出显示区域 - 修正参数方程:移除原代码中多余的
+np.pi相位偏移,确保外摆线的标准形状
内容的提问来源于stack exchange,提问作者Maroon Racoon
相关产品推荐
相关产品推荐

