diff --git a/sim_lab/solar_system_reality_size.py b/sim_lab/solar_system_reality_size.py new file mode 100644 index 0000000000000000000000000000000000000000..4b1a986407eda0c953115ab17e4328a6ae9c555c --- /dev/null +++ b/sim_lab/solar_system_reality_size.py @@ -0,0 +1,504 @@ +# -*- coding:utf-8 -*- +# title :模拟太阳系给天体真实时间和位置 +# description :模拟太阳系给天体真实时间和位置 +# author :Python超人 +# date :2023-07-23 +# link :https://gitcode.net/pythoncr/ +# python_version :3.8 +# ============================================================================== + +import numpy as np + +from bodies import Sun, Mercury, Venus, Earth, Mars, Asteroids, Jupiter, Saturn, Uranus, Neptune, Moon +from common.celestial_data_service import get_body_posvel, recalc_moon_position, calc_solar_acceleration, \ + set_solar_system_celestial_position, set_earth_rotation, get_reality_orbit_points +from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY, SECONDS_PER_HOUR, AU +from sim_scenes.func import ursina_run, create_sphere_sky, create_text_panel +from sim_scenes.universe_sim_scenes import UniverseSimScenes +from simulators.func import ext_fun_for_method +from simulators.ursina.entities.body_timer import TimeData +from simulators.ursina.ui.control_ui import ControlUI +from simulators.ursina.ursina_config import UrsinaConfig +from simulators.ursina.ursina_event import UrsinaEvent +from ursina import camera, application + +from simulators.ursina.ursina_mesh import create_orbit_line, create_orbit_by_points + + +class SolarSystemRealitySim(UniverseSimScenes): + """ + 以:武汉江城明珠豪生大酒店上面的球为太阳。直径:35米 + + """ + + def __init__(self): + """ + + @param debug_mode: 是否为调试模式 + """ + self.show_asteroids = False + self.clock_position_center = False + self.show_earth_clouds = False + self.debug_mode = False + self.recalc_moon_pos = True + + def create_bodies(self): + """ + 创建太阳系的天体 + @return: + """ + # 由于宇宙空间尺度非常大,如果按照实际的天体大小,则无法看到天体,因此需要对天体的尺寸进行放大 + + # 太阳 60倍、 + SUN_SIZE_SCALE = 50 + SIZE_SCALE_1 = 2000 + SIZE_SCALE_2 = 500 + SIZE_SCALE_3 = 1500 + + # 太阳缩放比例 + self.sun_size_scale = 0.04e2 if self.debug_mode else SUN_SIZE_SCALE + + # 地月缩放比例 + # 为了更好的展示效果,需要对月球的位置重新计算(使得地月距离放大,月球相对地球方向不变),重新计算位置后,地球和月球可以放大1000倍以上 + if self.recalc_moon_pos: # 重新计算月球位置 + self.earth_size_scale = 10e3 if self.debug_mode else 1e3 + self.moon_size_scale = 2e3 + else: + # 不重新计算,则地月的距离相对整个太阳系会非常近,因此,月球只放大了10倍 + self.earth_size_scale = 1e1 + self.moon_size_scale = 1e1 + + self.sun = Sun(name="太阳", size_scale=self.sun_size_scale) # 太阳 + self.sun.sim_d = 3500 # 35米 = 3500厘米 + self.sun.glows = None + self.mercury = Mercury(name="水星", size_scale=SIZE_SCALE_1) # 水星 + self.venus = Venus(name="金星", size_scale=SIZE_SCALE_1) # 金星 + self.earth = Earth(name="地球", texture="earth_hd.jpg", + rotate_angle=3.44, + size_scale=SIZE_SCALE_1) # 地球 + self.earth_clouds = Earth(name="地球云层", texture="transparent_clouds.png", show_trail=False, + rotate_angle=3.44, + size_scale=SIZE_SCALE_1 * 1.01) # 地球云层 + # self.moon = Moon(name="月球", size_scale=self.moon_size_scale) # 月球 + self.mars = Mars(name="火星", size_scale=SIZE_SCALE_1) # 火星 + # self.asteroids = Asteroids(size_scale=1e2, parent=self.sun, rotate_angle=-20) # 模拟的小行星带 + self.jupiter = Jupiter(name="木星", size_scale=SIZE_SCALE_2) # 木星 + self.saturn = Saturn(name="土星", size_scale=SIZE_SCALE_2) # 土星 + self.uranus = Uranus(name="天王星", size_scale=SIZE_SCALE_3) # 天王星 + self.neptune = Neptune(name="海王星", size_scale=SIZE_SCALE_3) # 海王星 + # 行星 + self.planets = [self.mercury, self.venus, self.earth, self.mars, + self.jupiter, self.saturn, self.uranus, self.neptune] + # 所有天体 + self.bodies = [self.sun] + self.planets # + [self.moon] + sim_d_r = self.sun.diameter / self.sun.sim_d + for body in self.bodies: + # print("%s的直径相当于:%.2fcm" % (body.name, body.diameter / sim_d_r)) + print("%s的位置相当于:%.2fkm" % (body.name, body.position[2] / (sim_d_r*100*1000))) + # 太阳的直径相当于:3500.00cm + # 水星的直径相当于:12.27cm + # 金星的直径相当于:30.44cm 排球、足球、篮球的直径分别是25~28厘米、21.96~22.04厘米、24.6厘米。 + # 地球的直径相当于:32.05cm 排球、足球、篮球的直径分别是25~28厘米、21.96~22.04厘米、24.6厘米。 + # 火星的直径相当于:17.04cm + # 木星的直径相当于:351.55cm + # 土星的直径相当于:292.82cm + # 天王星的直径相当于:127.54cm + # 海王星的直径相当于:123.80cm + + # 太阳的位置相当于:0.00km 武汉江城明珠豪生大酒店 35米 + # 水星的位置相当于:1.44km 武汉科学技术馆 武汉歌舞剧院 武汉剧院 武汉市中心医院 + # 金星的位置相当于:2.71km 龙王庙 徐家棚 不到中山公园 + # 地球的位置相当于:3.76km 超过中山公园 沙湖公园 南岸嘴江滩公园 + # 火星的位置相当于:5.72km 琴台大剧院 楚河汉街 首义广场 + # 木星的位置相当于:19.52km 后官湖 汤逊湖 + # 土星的位置相当于:35.73km 西:武汉野生动物王国 + # 天王星的位置相当于:72.21km 西:仙桃 + # 海王星的位置相当于:115.46km 西:天门 北:大悟 东:大希 南:赤壁 + + if self.show_earth_clouds: + self.bodies += [self.earth_clouds] + + # if self.show_asteroids: + # self.bodies += [self.asteroids] + exit() + + def init_earth(self): + """ + 初始化地球 + @return: + """ + # 让地球显示自转轴线 + # self.earth.rotate_axis_color = (255, 255, 50) + # 如果为调试模式,则太阳光对地球无效,方便查看 + if self.debug_mode: + self.earth.set_light_disable(True) + + def show_clock(self, dt): + """ + 显示时钟 + @param dt: 时间 datetime + @return: + """ + if self.clock_position_center: + position, origin = (0, .25), (0, 0), + else: + from ursina import window + aspect_ratio = window.aspect_ratio + position, origin = (0.5 * aspect_ratio - 0.3, -0.465), (-0.5, 0.5), + # position, origin = (0.60, -0.465), (-0.5, 0.5), + + ControlUI.current_ui.show_message(dt.strftime('%Y-%m-%d %H:%M:%S'), + position=position, + origin=origin, + font="verdana.ttf", + close_time=-1) + + def set_bodies_position(self, time_data: TimeData): + """ + 设置天体的位置(包含速度和加速度的信息) + @param time_data: + @return: + """ + t = self.start_time + time_data.total_days + set_solar_system_celestial_position(self.bodies, t, self.recalc_moon_pos) + + def on_ready(self): + """ + 事件绑定后,模拟器运行前会触发 + @return: + """ + # 运行前触发 + self.set_window_size((1919, 1080), False) + + self.sky = create_sphere_sky(scale=80000) + + self.create_orbit_lines() + + camera.clip_plane_near = 0.1 + camera.clip_plane_far = 100000 + + # camera.rotation_z = -20 + if self.debug_mode: + camera.fov = 30 # 调试时,拉近摄像机距离 + + # 需要按照时间和日期来控制地球的自转,所以删除控制地球自转的属性 + delattr(self.earth.planet, "rotation_speed") + delattr(self.earth.planet, "rotspeed") + + # 设置后,可以调整鼠标键盘的控制速度 + application.time_scale = 5 + + self.text_panel = create_text_panel(font="fonts/DroidSansFallback.ttf", font_scale=1.3) + self.text_panel.parent.scale_y /= 6 + self.text_panel.scale_y *= 6 + + def update_text_panel(self): + """ + 更新文字信息面板 + @param d_sun: + @return: + """ + from ursina import distance + d_sun = distance(self.sun.planet.position, camera.position) + d_sun = d_sun / UrsinaConfig.SCALE_FACTOR / AU + panel_text = "当前日距:%s AU" % "{:.2f}".format(d_sun) # .rjust(6, "0") + + self.text_panel.text = panel_text + + def on_timer_changed(self, time_data: TimeData): + """ + 事件绑定后,时时刻刻都会触发 + @param time_data: + @return: + """ + dt = time_data.get_datetime(str(self.start_time)) + # 设置天体的位置(包含速度和加速度的信息) + self.set_bodies_position(time_data) + # 保证地球的自转和北京时间同步 + set_earth_rotation(self.earth, dt) + # 显示时钟 + # self.show_clock(dt) + self.update_text_panel() + + def bind_events(self): + # 运行中,每时每刻都会触发 on_timer_changed + UrsinaEvent.on_timer_changed_subscription(self.on_timer_changed) + # 运行前会触发 on_ready + UrsinaEvent.on_ready_subscription(self.on_ready) + + def create_orbit_line(self, center_body, body, start_time, alpha=0.2): + import math + + if not hasattr(body, "orbital_days"): + return None + orbital_days = int(math.ceil(body.orbital_days)) + points = get_reality_orbit_points(type(body).__name__.lower(), + start_time=start_time, + days=orbital_days, + segments=100) + # print(points) + orbit_line = create_orbit_by_points(center_body.position, points, line_color=body.trail_color, + alpha=alpha, thickness=1) + return orbit_line + + def create_orbit_lines(self): + """ + 创建太阳系天体的真实轨迹(太阳和哈雷彗星除外) + @return: + """ + self.orbit_lines = [] + for body in self.bodies[1:]: + alpha = 1 + orbit_line = self.create_orbit_line(self.sun, body, self.start_time, alpha=alpha) + if orbit_line is not None: + self.orbit_lines.append(orbit_line) + + def run(self, + debug_mode=False, + start_time=None, + dt=None, + # show_asteroids=False, + show_earth_clouds=False, + recalc_moon_pos=True, + clock_position_center=False): + """ + 模拟运行 + @param debug_mode: 是否调试模式 + @param start_time: 运行的开始时间 + @param dt: 运行速度(dt的值为秒数,表示1秒相当于dt的秒数) + @param show_earth_clouds: 地球是否显示云层(图片效果,不是真实的云层) + @param recalc_moon_pos: 为了更好的展示效果,需要对月球的位置重新计算(使得地月距离放大,月球相对地球方向不变) + @param clock_position_center: 时钟是否显示在中间 + @return: + """ + self.recalc_moon_pos = recalc_moon_pos + self.debug_mode = debug_mode + self.clock_position_center = clock_position_center + # self.show_asteroids = show_asteroids + self.show_earth_clouds = show_earth_clouds + # 创建太阳系天体 + self.create_bodies() + # glows = (glow_num:10, glow_scale:1.03 glow_alpha:0.1~1) + # self.sun.glows = (4, 1.005, 0.1) + + # 对地球进行初始化 + self.init_earth() + # 绑定事件 + self.bind_events() + + from astropy.time import Time + from datetime import datetime + # 开始时间为空,则默认为当前时间 + if start_time is None: + self.start_time = Time.now() # 获取默认开始时间为当前时间 + elif isinstance(start_time, str): + self.start_time = Time(datetime.strptime(start_time + '+0800', '%Y-%m-%d %H:%M:%S%z'), + format='datetime') + + # from common.image_utils import find_texture + # self.sky_texture = find_texture("bg_pan.jpg", None) + # if self.sky_texture is None: + # cosmic_bg = None + # else: + # cosmic_bg = '' + if dt is None: + dt = 1 # 1秒=1秒 + + self.init_steps() + # 使用 ursina 查看的运行效果 + # 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹 + # position = 左-右+、上+下-、前+后- + ursina_run(self.bodies, dt, + position=(0, 0.2 * AU, -3 * AU), + gravity_works=False, # 关闭万有引力的计算 + show_grid=False, + cosmic_bg='', + show_camera_info=False, + show_exit_button=False, + show_control_info=False, + timer_enabled=True) + + def init_steps(self): + self.step_index = 0 + self.steps = [ + self.set_bodies_as_real_scale, + lambda: self.enabled_orbit_lines(), + lambda: self.recover_body_scale(self.sun), + lambda: self.recover_body_scale([self.earth, self.mercury, self.venus, self.mars, self.earth_clouds]), + lambda: self.recover_body_scale([self.jupiter, self.saturn]), + lambda: self.recover_body_scale([self.uranus, self.neptune]), + lambda: self.recover_sky(), + # lambda: self.recover_run(), + self.set_bodies_as_real_scale_animation + ] + + def enabled_orbit_line(self, orbit_line): + from ursina import Vec4 + orbit_line.alpha = 0 + orbit_line.color *= Vec4(1, 1, 1, 0) + + # orbit_line.origin_color = orbit_line.color + + def orbit_line_update(): + alpha = orbit_line.alpha + alpha += 0.01 + print(alpha) + if alpha > orbit_line.origin_alpha: + alpha = orbit_line.origin_alpha + orbit_line.update = lambda: None + orbit_line.alpha = alpha + orbit_line.color = Vec4(orbit_line.color[0], orbit_line.color[1], orbit_line.color[2], alpha) + print(orbit_line, orbit_line.alpha) + + orbit_line.update = orbit_line_update + orbit_line.enabled = True + + def enabled_orbit_lines(self): + for orbit_line in self.orbit_lines: + # orbit_line.origin_alpha = orbit_line.alpha + # orbit_line.alpha = 0 + + self.enabled_orbit_line(orbit_line) + + def scale_animation(self, body): + size_scale, scale_x = self.body_scale_dict[body] + scale_inc = (scale_x - body.planet.scale_x) / 200.0 + + def update_scale(): + # body.planet.origin_update() + planet_scale_x = body.planet.scale_x + planet_scale_x += scale_inc + if planet_scale_x > scale_x: + planet_scale_x = scale_x + body.planet.update = body.planet.origin_update + body.planet.scale = planet_scale_x + + body.planet.update = update_scale + + def recover_run(self): + pass + # for body in self.bodies: + # body.planet.update = body.planet.origin_update + + def recover_sky(self): + def update_sky(): + alpha = self.sky.alpha + alpha += 0.01 + if alpha > 1.0: + alpha = 1.0 + self.sky.update = lambda: None + self.sky.alpha = alpha + + self.sky.update = update_sky + + def recover_body_scale(self, body_or_bodies): + if not isinstance(body_or_bodies, list): + bodies = [body_or_bodies] + else: + bodies = body_or_bodies + for body in bodies: + self.scale_animation(body) + from ursina import invoke + + def recover_run_speed(): + UrsinaConfig.run_speed_factor = 1 + + invoke(function=recover_run_speed, delay=3.0) + + def set_body_as_real_scale(self, body): + UrsinaConfig.run_speed_factor = 0.01 + body.planet.origin_update = body.planet.update + + # body.planet.update = lambda: None + + def update_scale(): + size_scale, scale_x = self.body_scale_dict[body] + body.planet.scale = scale_x / size_scale + + ext_fun_for_method(body.planet, after_run_fun=update_scale) + + def set_body_as_real_scale_animation(self, body): + UrsinaConfig.run_speed_factor = 0.01 + body.planet.origin_update = body.planet.update + size_scale, scale_x = self.body_scale_dict[body] + scale_inc = (scale_x - scale_x / size_scale) / 200.0 + + # scale_inc = (scale_x / size_scale) / 10.0 + + def update_scale(): + planet_scale_x = body.planet.scale_x + planet_scale_x -= scale_inc + + if planet_scale_x <= scale_x / size_scale: + planet_scale_x = scale_x / size_scale + body.planet.update = lambda: None # body.planet.origin_update + + body.planet.scale = planet_scale_x # scale_x / size_scale + + body.planet.update = update_scale + # ext_fun_for_method(body.planet, after_run_fun=update_scale) + + def set_bodies_as_real_scale(self): + self.save_body_scale() + for body in self.bodies: + self.set_body_as_real_scale(body) + + for orbit_line in self.orbit_lines: + orbit_line.origin_alpha = orbit_line.alpha + # orbit_line.alpha = 0 + orbit_line.enabled = False + + self.sky.alpha = 0 + + def set_bodies_as_real_scale_animation(self): + # self.save_body_scale() + for body in self.bodies: + self.set_body_as_real_scale_animation(body) + + # for orbit_line in self.orbit_lines: + # orbit_line.origin_alpha = orbit_line.alpha + # # orbit_line.alpha = 0 + # orbit_line.enabled = False + + self.sky.alpha = 0 + + def save_body_scale(self): + self.body_scale_dict = {} + for body in self.bodies: + self.body_scale_dict[body] = (body.size_scale, body.planet.scale_x) + + print(self.body_scale_dict) + + def input(self, key): + # print(key) + if key == "enter up": + if self.step_index < len(self.steps): + fun = self.steps[self.step_index] + fun() + self.step_index += 1 + + +if __name__ == '__main__': + # 以下展示的效果为太阳系真实的时间和位置 + sim = SolarSystemRealitySim() + + + def input(key): + sim.input(key) + + + sim.run( + # debug_mode=True, # 是否调试模式 + dt=SECONDS_PER_DAY, # 1秒=1天 + # dt=SECONDS_PER_HOUR, # 1秒=1小时 + start_time='1985-02-28 00:00:00', + # start_time='2025-01-01 00:00:00', + # start_time='2050-01-01 12:00:00', # 指定运行的开始时间,不指定为当前时间 + # 网上没有找到精确的日期,宇宙模拟器展示大概2040年8、9月份 + # start_time='2040-08-15 12:00:00', # 金木水火土五星连珠的时间 # https://baijiahao.baidu.com/s?id=1776120995339598449 + # start_time='2049-01-01 12:00:00', # 九星连珠的时间 # https://988892.com/qiwenyishi/2023061960711.html + # start_time='2149-12-10 12:00:00', # 九星连珠的时间 # https://baijiahao.baidu.com/s?id=1654160345900112362 + show_earth_clouds=True, # 地球是否显示云层(图片效果,不是真实的云层) + # recalc_moon_pos=False, # 为了更好的展示效果,需要对月球的位置重新计算(使得地月距离放大,月球相对地球方向不变) + clock_position_center=True # 时钟是否显示在中间 + ) diff --git a/sim_scenes/fiction/transformed_mars_ani_3d.py b/sim_scenes/fiction/transformed_mars_ani_3d.py index 86a60beeb6a83391463cd916fbceab8d32685c5d..518a56139b95db4964e4d420ac17e8925b66844a 100644 --- a/sim_scenes/fiction/transformed_mars_ani_3d.py +++ b/sim_scenes/fiction/transformed_mars_ani_3d.py @@ -55,7 +55,7 @@ def create_space_station(mars_radius): space_station = ScifiSpaceStation(name="空间站", size_scale=0.5, # init_position=[0.46 * mars_radius, 0, -3.55e4], - init_position=[0.46 * mars_radius, 300, -4.40e4], + init_position=[0.46 * mars_radius, 300, -5.1e4], init_velocity=[0, 0, 0]) \ .set_ignore_gravity(True) return space_station