mpl_simulator.py 6.3 KB
Newer Older
M
march3 已提交
1
# -*- coding:utf-8 -*-
M
march3 已提交
2 3
# title           :matplotlib天体运行模拟器
# description     :matplotlib天体运行模拟器
M
march3 已提交
4
# author          :Python超人
M
march3 已提交
5 6
# date            :2023-02-11
# link            :https://gitcode.net/pythoncr/
M
march3 已提交
7 8 9 10
# python_version  :3.8
# ==============================================================================
import matplotlib.pyplot as plt
import matplotlib.animation as animation
M
march3 已提交
11
import matplotlib.colors as mcolors
M
march3 已提交
12 13
from simulators.simulator import Simulator
from common.system import System
M
march3 已提交
14
from common.consts import AU
M
march3 已提交
15
from simulators.views.mpl_view import MplView
M
march3 已提交
16
from simulators.func import create_fig_ax, update_ax_styles
M
march3 已提交
17
import numpy as np
M
march3 已提交
18
import copy
M
march3 已提交
19 20 21 22 23 24 25 26 27 28 29 30 31

plt.rcParams['font.sans-serif'] = ['SimHei']  # 步骤一(替换默认sans-serif字体)
plt.rcParams['axes.unicode_minus'] = False  # 步骤二(解决坐标轴负数的负号显示问题)


class MplSimulator(Simulator):
    """
    matplotlib天体运行模拟器
    """

    def __init__(self, bodies_sys: System):
        super().__init__(bodies_sys, MplView)

M
march3 已提交
32
    def save_as_gif(self, dt, gif_max_frame=200, gif_file_name='bodies_run.gif', styles={}):
M
march3 已提交
33
        """
M
march3 已提交
34 35
        保存 GIF 文件
        :param dt: 单位:秒,按时间差进行演变,值越小越精确,但演变速度会慢。
M
march3 已提交
36
        :param gif_max_frame: 导出的 gif 文件的画面帧数
M
march3 已提交
37
        :param gif_file_name: 导出的 gif 文件名
M
march3 已提交
38 39
        :return:
        """
M
march3 已提交
40 41 42

        fig, ax = create_fig_ax()

M
march3 已提交
43
        views_frames = []
M
march3 已提交
44
        for i in range(gif_max_frame):
M
march3 已提交
45
            self.evolve(dt)
M
march3 已提交
46 47 48
            body_views = copy.deepcopy(self.body_views)
            views_frames.append(body_views)

M
march3 已提交
49 50
        def update(num):
            body_views = views_frames[num]
M
march3 已提交
51
            print("\rGIF 生成进度:%d/%d %.2f" % (num + 1, gif_max_frame, ((num + 1) / gif_max_frame) * 100) + "%", end='')
M
march3 已提交
52
            return self.show_figure(ax, body_views, pause=0, update_ax=update_ax_styles, styles=styles)
M
march3 已提交
53 54 55 56

        ani = animation.FuncAnimation(fig=fig, func=update, frames=np.arange(0, gif_max_frame), interval=1)
        ani.save(gif_file_name)

M
march3 已提交
57
    def run(self, dt, **kwargs):
M
march3 已提交
58 59 60
        """

        :param dt: 单位:秒,按时间差进行演变,值越小越精确,但演变速度会慢。
M
march3 已提交
61 62 63
        :param kwargs:
            gif_file_name: 导出的 gif 文件名,如果为空,则显示动画
            gif_max_frame: 导出的 gif 文件的画面帧数
M
march3 已提交
64 65
        :return:
        """
M
march3 已提交
66 67
        gif_file_name = kwargs["gif_file_name"] if "gif_file_name" in kwargs else None
        gif_max_frame = kwargs["gif_max_frame"] if "gif_max_frame" in kwargs else None
M
march3 已提交
68
        styles = kwargs["styles"] if "styles" in kwargs else {}
M
march3 已提交
69

M
march3 已提交
70
        if gif_file_name is not None:
M
march3 已提交
71
            self.save_as_gif(dt, gif_max_frame=gif_max_frame, gif_file_name=gif_file_name, styles=styles)
M
march3 已提交
72 73
            return

M
march3 已提交
74 75
        fig, ax = create_fig_ax()

M
march3 已提交
76 77
        # TODO: 注意:显示动态图,需先进行以下设置:
        # Pycharm::File –> Settings –> Tools –> Python Scientific –> Show plots in tool window(取消打勾)
M
march3 已提交
78

M
march3 已提交
79 80 81
        while True:
            self.evolve(dt)
            body_views = copy.deepcopy(self.body_views)
M
march3 已提交
82
            self.show_figure(ax, body_views, pause=0.1, update_ax=update_ax_styles, styles=styles)
M
march3 已提交
83

M
march3 已提交
84
    def show_figure(self, ax, bodies, pause=0.1, update_ax=None, styles={}):
M
march3 已提交
85 86 87 88 89 90 91
        """

        :param ax:
        :param bodies:
        :param pause:
        :return:
        """
M
march3 已提交
92 93 94
        if update_ax is not None:
            # 更新 ax
            update_ax(ax, styles)
M
march3 已提交
95 96

        for idx, body in enumerate(bodies):
三月三net's avatar
三月三net 已提交
97 98 99 100
            if hasattr(body, "torus_stars"):
                # 暂不支持环状小行星群
                continue

M
march3 已提交
101 102 103 104 105
            if body.is_fixed_star:
                color = 'red'
            else:
                color = 'blue'
            # size = 800 if str(body.name).lower().startswith("sun") else 500
M
march3 已提交
106
            size = body.raduis * body.size_scale / 80000
M
march3 已提交
107 108 109
            # size = pow(body.raduis / AU * body.size_scale,3)
            pos = body.position / AU

M
march3 已提交
110 111
            # 天体
            ax.scatter(pos[0], pos[1], pos[2], color=color, s=size, alpha=0.8)
M
march3 已提交
112 113 114
            # for _his_pos in body.his_position():
            #     ax.scatter3D(_his_pos[0], _his_pos[1], _his_pos[2], color=color, alpha=0.5)
            # ax.scatter(his_pos[0], his_pos[1], his_pos[2], color=colors[idx], s=10)
M
march3 已提交
115
            his_pos = np.array(body.his_position) / AU
M
march3 已提交
116 117
            tail_len = len(his_pos)
            if tail_len > 1:
M
march3 已提交
118
                _his_pos = list(zip(*his_pos))
M
march3 已提交
119 120
                # 历史轨迹线
                ax.plot3D(_his_pos[0], _his_pos[1], _his_pos[2], color=color, alpha=0.5)
M
march3 已提交
121

M
march3 已提交
122
            z_range = ax.get_zlim()[1] - ax.get_zlim()[0]
三月三net's avatar
三月三net 已提交
123
            ax.text(pos[0], pos[1], pos[2] + size * (z_range / 5000), s=body.name, color=color, fontsize=12)
M
march3 已提交
124

M
march3 已提交
125 126
        if pause > 0:
            plt.pause(pause)
M
march3 已提交
127 128 129 130 131 132 133 134


if __name__ == '__main__':
    from scenes.func import mpl_run
    from bodies import Sun, Earth
    from common.consts import SECONDS_PER_WEEK

    """
M
march3 已提交
135
    3个太阳、1个地球
M
march3 已提交
136 137
    """
    bodies = [
M
march3 已提交
138 139 140 141 142 143 144 145
        Sun(name='太阳1', mass=1.5e30, init_position=[849597870.700, 0, 0], init_velocity=[0, 7.0, 0],
            size_scale=5e1, texture="sun1.jpg"),  # 太阳放大 100 倍
        Sun(name='太阳2', mass=2e30, init_position=[0, 0, 0], init_velocity=[0, -8.0, 0],
            size_scale=5e1, texture="sun2.jpg"),  # 太阳放大 100 倍
        Sun(name='太阳3', mass=2.5e30, init_position=[0, -849597870.700, 0], init_velocity=[18.0, 0, 0],
            size_scale=5e1, texture="sun2.jpg"),  # 太阳放大 100 倍
        Earth(name='地球', init_position=[0, -349597870.700, 0], init_velocity=[15.50, 0, 0],
              size_scale=4e3, distance_scale=1),  # 地球放大 4000 倍,距离保持不变
M
march3 已提交
146
    ]
M
march3 已提交
147
    # 保存 GIF,参数为指定保存 GIF 文件以及文件的画面帧数
M
march3 已提交
148
    gif_file_name, gif_max_frame = 'bodies_run.gif', 100
M
march3 已提交
149 150 151 152

    # 只显示动画,不保存 GIF 文件。注释掉以下代码,则使用上面的参数
    # TODO: 注意:显示动态图,需先进行以下设置:
    # Pycharm::File –> Settings –> Tools –> Python Scientific –> Show plots in tool window(取消打勾)
M
march3 已提交
153
    gif_file_name, gif_max_frame = None, None
M
march3 已提交
154 155

    mpl_run(bodies, SECONDS_PER_WEEK, gif_file_name, gif_max_frame)