Python月球、地球、太阳三天体联动一个月的月相图

本文介绍了一个使用Python Matplotlib库创建的月相变化动画程序。该程序通过绘制地球、月球轨道和太阳光照,模拟展示了月球绕地球公转时不同位置呈现的月相变化(包括新月、上弦月、满月等下弦月等)。动画采用红色月球配合白色受光面的视觉效果,准确表现了月相变化的原理。程序设置了120帧动画循环,每帧计算月球轨道角度和相应月相名称,最终输出为GIF格式的月相变化演示动画。该实现结合了天文学知识和数据可视化技术,为理解月相变化提供了直观的教学工具。

python 复制代码
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
from matplotlib.animation import FuncAnimation, PillowWriter
from matplotlib import rcParams

# 中文字体
rcParams["font.sans-serif"] = ["SimHei", "Microsoft YaHei", "Arial Unicode MS"]
rcParams["axes.unicode_minus"] = False

# ----------------------------
# 参数设置
# ----------------------------
fig, ax = plt.subplots(figsize=(8, 8))
ax.set_aspect("equal")
ax.set_xlim(-4.2, 4.8)
ax.set_ylim(-4.2, 4.2)
ax.axis("off")

orbit_r = 2.8
earth_r = 0.5
moon_r = 0.28
sun_x, sun_y = 4.2, 3.0
frames = 120

# ----------------------------
# 绘制"红色月球 + 白色投影"
# ----------------------------
def draw_moon_red_white(ax, x, y, r, phase_deg):
    # 红色底盘
    ax.add_patch(Circle((x, y), r, color="#CC0000", ec="black", lw=1.2))

    # 新月
    if phase_deg == 0:
        return

    # 满月:整颗变白
    if phase_deg == 180:
        ax.add_patch(Circle((x, y), r, color="white", ec="black", lw=1.2))
        return

    # 白色"投影/受光面"
    phi = np.deg2rad(phase_deg)

    if phase_deg < 180:
        # 盈月:右侧白
        ax.add_patch(Circle((x, y), r, color="white", ec="none"))
        shift = r * np.cos(phi)
        ax.add_patch(Circle((x - shift, y), r, color="#CC0000", ec="none"))
    else:
        # 亏月:左侧白
        ax.add_patch(Circle((x, y), r, color="white", ec="none"))
        shift = r * np.cos(np.pi - phi)
        ax.add_patch(Circle((x + shift, y), r, color="#CC0000", ec="none"))

    # 外轮廓
    ax.add_patch(Circle((x, y), r, fill=False, ec="black", lw=1.2))

# ----------------------------
# 月相文字
# ----------------------------
def get_phase_name(angle_deg):
    if angle_deg < 22.5 or angle_deg >= 337.5:
        return "新月"
    elif angle_deg < 67.5:
        return "蛾眉月"
    elif angle_deg < 112.5:
        return "上弦月"
    elif angle_deg < 157.5:
        return "盈凸月"
    elif angle_deg < 202.5:
        return "满月"
    elif angle_deg < 247.5:
        return "亏凸月"
    elif angle_deg < 292.5:
        return "下弦月"
    else:
        return "亏眉月"

# ----------------------------
# 动画更新函数
# ----------------------------
def update(frame):
    ax.clear()
    ax.set_aspect("equal")
    ax.set_xlim(-4.2, 4.8)
    ax.set_ylim(-4.2, 4.2)
    ax.axis("off")
    ax.set_facecolor("white")

    # 当前月球轨道角度
    angle_deg = frame * 360 / frames
    theta = np.deg2rad(angle_deg)

    # 月球位置
    moon_x = orbit_r * np.cos(theta)
    moon_y = orbit_r * np.sin(theta)

    # 月相角:0°新月,180°满月
    phase_deg = angle_deg if angle_deg <= 180 else 360 - angle_deg

    # 太阳光
    ax.annotate(
        "",
        xy=(-3.0, 3.0),
        xytext=(4.0, 3.0),
        arrowprops=dict(arrowstyle="->", lw=2.5, color="orange")
    )
    ax.text(4.05, 3.15, "太阳光", color="orange", fontsize=12, ha="right")

    # 太阳
    sun = Circle((sun_x, sun_y), 0.35, color="gold", ec="black", lw=1.2)
    ax.add_patch(sun)
    ax.text(sun_x, sun_y, "太阳", ha="center", va="center", fontsize=11, weight="bold")

    # 地球
    earth = Circle((0, 0), earth_r, color="#2E86DE", ec="black", lw=1.5)
    ax.add_patch(earth)
    ax.text(0, 0, "地球", ha="center", va="center", color="white", fontsize=13, weight="bold")

    # 轨道
    orbit = Circle((0, 0), orbit_r, fill=False, ls="--", ec="gray", lw=1.2)
    ax.add_patch(orbit)

    # 地月连线
    ax.plot([0, moon_x], [0, moon_y], color="gray", lw=1, ls=":")

    # 月球
    draw_moon_red_white(ax, moon_x, moon_y, moon_r, phase_deg)
    ax.text(moon_x, moon_y - 0.45, "月球", ha="center", va="top", fontsize=11)

    # 文字说明
    phase_text = get_phase_name(angle_deg)
    ax.text(-4.0, 3.6, f"月相动画:{phase_text}", fontsize=15, weight="bold")
    ax.text(2.1, -3.7, f"月球轨道角度:{int(angle_deg)}°", fontsize=11)

    # 轨道方向箭头
    ax.annotate(
        "",
        xy=(orbit_r * np.cos(np.deg2rad(70)), orbit_r * np.sin(np.deg2rad(70))),
        xytext=(orbit_r * np.cos(np.deg2rad(30)), orbit_r * np.sin(np.deg2rad(30))),
        arrowprops=dict(arrowstyle="->", lw=1.8, color="black")
    )

# ----------------------------
# 生成 GIF
# ----------------------------
anim = FuncAnimation(fig, update, frames=frames, interval=80)
gif_name = "moon_phase_red_white.gif"
anim.save(gif_name, writer=PillowWriter(fps=12))
print(f"已生成动画文件:{gif_name}")
plt.close(fig)
相关推荐
小陈工2 小时前
Python开源代码管理避坑实战:从Git高级操作到Docker环境配置
开发语言·git·python·安全·docker·开源·源代码管理
小陈工2 小时前
2026年3月27日技术资讯洞察:量子计算密码突破、硬件安全新范式与三月网络安全警报
服务器·python·安全·web安全·单元测试·集成测试·量子计算
倾一生爱恋换一世纯真2 小时前
使用python代码生成ragas的测试文档testset.json
人工智能·python·ragas·rag测试
繁星星繁2 小时前
Python基础语法(一)
c++·笔记·python
未来转换2 小时前
Python 框架 Django 从入门到精通
数据库·python·django
weixin_457760002 小时前
KenLM简介及安装使用
python·kenlm
B站_计算机毕业设计之家2 小时前
计算机毕业设计:汽车数据可视化与后台管理平台 Django框架 requests爬虫 可视化 车辆 数据分析 大数据 机器学习(建议收藏)✅
python·算法·机器学习·信息可视化·django·汽车·课程设计
ai安歌2 小时前
学生管理系统——Django学生管理系统架构设计与实现:从零构建现代化Web应用
前端·python·django
子夜四时歌3 小时前
Python详细安装与环境搭建
开发语言·python