1. 引言
在计算机图形学、物理模拟和数学可视化中,点的旋转是一个基础且重要的概念。本文将使用 Python 的 Matplotlib 库,通过动画直观地演示一个二维点如何绕原点进行旋转。我们将从旋转矩阵的数学原理出发,逐步编写代码,最终生成一个平滑的旋转动画。
2. 旋转矩阵原理
二维平面中,一个点(x, y)绕原点逆时针旋转 θ 弧度后,新坐标(x', y')可以通过旋转矩阵计算:
[x'] [cosθ -sinθ] [x] [y'] = [sinθ cosθ] [y]展开后得到:
x' = x * cosθ - y * sinθy' = x * sinθ + y * cosθ
这就是我们代码中rotate函数的数学基础。
3. 代码实现
3.1 导入库
importnumpyasnp# 导入 numpy,用于数学计算(三角函数、数组)importmatplotlib.pyplotasplt# 导入 matplotlib 绘图库,用于绘制图像frommatplotlib.animationimportFuncAnimation# 导入动画模块,用于生成逐帧动画3.2 定义旋转函数
# 旋转半径R=2.0# 定义初始点距离原点的距离(半径)# 初始点 (R, 0)'''[x_f; y_f] = [[cos(θ), -sin(θ)], [sin(θ), cos(θ)]] * [x_m; y_m]'''# 二维旋转矩阵defrotate(px,py,theta):"""将点 (px, py) 绕原点旋转 theta 弧度,返回旋转后的新坐标。"""c=np.cos(theta)# 计算旋转角度的余弦值s=np.sin(theta)# 计算旋转角度的正弦值x=px*c-py*s# 旋转公式:新 x = px*cosθ - py*sinθy=px*s+py*c# 旋转公式:新 y = px*sinθ + py*cosθreturnx,y# 返回旋转后的坐标 (x, y)3.3 初始化画布
# 创建画布fig,ax=plt.subplots(figsize=(6,6))# 创建 6x6 英寸的画布和坐标轴对象ax.set_xlim(-R-0.4,R+0.4)# 设置 x 轴显示范围,留出 0.4 的边距ax.set_ylim(-R-0.4,R+0.4)# 设置 y 轴显示范围,留出 0.4 的边距ax.set_aspect("equal")# 设置 x、y 轴比例相等,保证圆形不变形ax.grid(True,alpha=0.3)# 开启网格线,透明度设为 0.3ax.axhline(0,c="black",lw=1)# 绘制 x 轴(y=0 的水平线)ax.axvline(0,c="black",lw=1)# 绘制 y 轴(x=0 的垂直线)ax.set_title("2D Point Rotate Around Origin")# 设置图像标题# 绘图对象point,=ax.plot([],[],"ro",markersize=8)# 创建红点对象,初始为空,尺寸 8,代表旋转的点3.4 定义动画函数
definit():"""初始化函数:blit 模式下先绘制一帧空白画面。"""point.set_data([],[])# 红点初始为空return(point,)# 必须返回可迭代的 Artist 序列(单元素元组)defupdate(frame):"""动画每一帧的更新函数,frame 为当前帧编号。"""theta=np.radians(frame)# 将帧编号(角度值)从度转换为弧度x,y=rotate(R,0,theta)# 计算初始点 (R, 0) 旋转 theta 后的坐标point.set_data([x],[y])# 更新红点的位置return(point,)# 必须返回可迭代的 Artist 序列(单元素元组)3.5 创建并运行动画
# 动画ani=FuncAnimation(# 创建动画对象fig,update,frames=np.arange(0,360,2),# 帧从 0° 到 360°,每 2° 一帧init_func=init,# 初始化函数:blit 模式用它绘制初始空白帧interval=30,blit=True,repeat=True# 每帧间隔 30ms;blit=True 加快渲染;循环播放)plt.show()# 显示动画窗口4. 代码解析
- 旋转矩阵:
rotate函数实现了二维旋转矩阵的乘法运算。 - 动画参数:
frames=np.arange(0, 360, 2):生成从 0° 到 360°,步长为 2° 的帧序列,共 180 帧。interval=30:每帧间隔 30 毫秒,动画速度约为 33 帧/秒。blit=True:只重绘变化的部分(红点),大幅提升渲染效率。repeat=True:动画播放完毕后自动循环。
- 坐标轴设置:
ax.set_aspect("equal")确保 x 和 y 轴比例一致,旋转轨迹呈现完美的圆形。
5. 运行效果
运行上述代码后,会弹出一个窗口,显示一个红点以原点为中心、半径为 2 的圆形轨迹匀速旋转。你可以通过修改R的值来改变旋转半径,或调整frames和interval参数来改变旋转速度和流畅度。
6. 扩展思考
- 绕任意点旋转:修改
rotate函数,先平移坐标系使旋转中心移至原点,旋转后再平移回去。 - 绘制轨迹:在
update函数中记录历史位置并绘制连线,可直观看到圆形路径。 - 三维旋转:使用类似原理,可以扩展到三维空间绕轴旋转。
7. 总结
本文通过一个简洁的 Python 动画示例,演示了二维旋转矩阵的应用。代码结构清晰,注释详细,适合初学者理解旋转的数学原理和 Matplotlib 动画的基本用法。你可以在此基础上进行修改和扩展,实现更复杂的图形动画效果。
附
importnumpyasnp# 导入 numpy,用于数学计算(三角函数、数组)importmatplotlib.pyplotasplt# 导入 matplotlib 绘图库,用于绘制图像frommatplotlib.animationimportFuncAnimation# 导入动画模块,用于生成逐帧动画# 旋转半径R=2.0# 定义初始点距离原点的距离(半径)# 初始点 (R, 0)'''[x_f; y_f] = [[cos(θ), -sin(θ)], [sin(θ), cos(θ)]] * [x_m; y_m]'''# 二维旋转矩阵defrotate(px,py,theta):"""将点 (px, py) 绕原点旋转 theta 弧度,返回旋转后的新坐标。"""c=np.cos(theta)# 计算旋转角度的余弦值s=np.sin(theta)# 计算旋转角度的正弦值x=px*c-py*s# 旋转公式:新 x = px*cosθ - py*sinθy=px*s+py*c# 旋转公式:新 y = px*sinθ + py*cosθreturnx,y# 返回旋转后的坐标 (x, y)# 创建画布fig,ax=plt.subplots(figsize=(6,6))# 创建 6x6 英寸的画布和坐标轴对象ax.set_xlim(-R-0.4,R+0.4)# 设置 x 轴显示范围,留出 0.4 的边距ax.set_ylim(-R-0.4,R+0.4)# 设置 y 轴显示范围,留出 0.4 的边距ax.set_aspect("equal")# 设置 x、y 轴比例相等,保证圆形不变形ax.grid(True,alpha=0.3)# 开启网格线,透明度设为 0.3ax.axhline(0,c="black",lw=1)# 绘制 x 轴(y=0 的水平线)ax.axvline(0,c="black",lw=1)# 绘制 y 轴(x=0 的垂直线)ax.set_title("2D Point Rotate Around Origin")# 设置图像标题# 绘图对象point,=ax.plot([],[],"ro",markersize=8)# 创建红点对象,初始为空,尺寸 8,代表旋转的点definit():"""初始化函数:blit 模式下先绘制一帧空白画面。"""point.set_data([],[])# 红点初始为空return(point,)# 必须返回可迭代的 Artist 序列(单元素元组)defupdate(frame):"""动画每一帧的更新函数,frame 为当前帧编号。"""theta=np.radians(frame)# 将帧编号(角度值)从度转换为弧度x,y=rotate(R,0,theta)# 计算初始点 (R, 0) 旋转 theta 后的坐标point.set_data([x],[y])# 更新红点的位置return(point,)# 必须返回可迭代的 Artist 序列(单元素元组)# ani = FuncAnimation(# fig, # ① 画布对象:告诉动画"在哪块画布上播放"# update, # ② 更新函数:告诉动画"每帧调用哪个函数来画图"# frames=np.arange(0, 360, 2), # ③ 帧序列:告诉动画"一共有多少帧"# interval=30, # ④ 时间间隔:每帧之间隔多少毫秒(30ms)# blit=True, # ⑤ 是否只重绘变化的部分# repeat=True # ⑥ 播完一遍后是否循环# )# 动画ani=FuncAnimation(# 创建动画对象fig,update,frames=np.arange(0,360,2),# 帧从 0° 到 360°,每 2° 一帧init_func=init,# 初始化函数:blit 模式用它绘制初始空白帧interval=30,blit=True,repeat=True# 每帧间隔 30ms;blit=True 加快渲染;循环播放)plt.show()# 显示动画窗口