云计算百科
云计算领域专业知识百科平台

旋转矩阵:二维点绕原点旋转

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 导入库

import numpy as np # 导入 numpy,用于数学计算(三角函数、数组)
import matplotlib.pyplot as plt # 导入 matplotlib 绘图库,用于绘制图像
from matplotlib.animation import FuncAnimation # 导入动画模块,用于生成逐帧动画

3.2 定义旋转函数

# 旋转半径
R = 2.0 # 定义初始点距离原点的距离(半径)
# 初始点 (R, 0)

'''[x_f; y_f] = [[cos(θ), -sin(θ)], [sin(θ), cos(θ)]] * [x_m; y_m]'''
# 二维旋转矩阵
def rotate(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θ
return x, y # 返回旋转后的坐标 (x, y)

3.3 初始化画布

# 创建画布
fig, ax = plt.subplots(figsize=(6, 6)) # 创建 6×6 英寸的画布和坐标轴对象
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.3
ax.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 定义动画函数

def init():
"""初始化函数:blit 模式下先绘制一帧空白画面。"""
point.set_data([], []) # 红点初始为空
return (point,) # 必须返回可迭代的 Artist 序列(单元素元组)

def update(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 动画的基本用法。你可以在此基础上进行修改和扩展,实现更复杂的图形动画效果。

    import numpy as np # 导入 numpy,用于数学计算(三角函数、数组)
    import matplotlib.pyplot as plt # 导入 matplotlib 绘图库,用于绘制图像
    from matplotlib.animation import FuncAnimation # 导入动画模块,用于生成逐帧动画

    # 旋转半径
    R = 2.0 # 定义初始点距离原点的距离(半径)
    # 初始点 (R, 0)

    '''[x_f; y_f] = [[cos(θ), -sin(θ)], [sin(θ), cos(θ)]] * [x_m; y_m]'''
    # 二维旋转矩阵
    def rotate(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θ
    return x, y # 返回旋转后的坐标 (x, y)

    # 创建画布
    fig, ax = plt.subplots(figsize=(6, 6)) # 创建 6×6 英寸的画布和坐标轴对象
    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.3
    ax.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,代表旋转的点

    def init():
    """初始化函数:blit 模式下先绘制一帧空白画面。"""
    point.set_data([], []) # 红点初始为空
    return (point,) # 必须返回可迭代的 Artist 序列(单元素元组)

    def update(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() # 显示动画窗口

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 旋转矩阵:二维点绕原点旋转
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!