# -*- coding:utf-8 -*- # title :太阳系中消失的行星 # description :太阳系中消失的行星 # author :Python超人 # date :2023-12-05 # link :https://gitcode.net/pythoncr/ # python_version :3.9 # ============================================================================== import time from bodies import Sun, Mercury, Venus, Earth, Mars, Moon, Ceres, Jupiter, Saturn, Uranus, Neptune, Pluto, Asteroids from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY, SECONDS_PER_MONTH, SECONDS_PER_YEAR, AU from sim_scenes.func import mayavi_run, ursina_run, create_sphere_sky from sim_scenes.universe_sim_scenes import UniverseSimScenes from simulators.func import ext_fun_for_method from simulators.ursina.ursina_event import UrsinaEvent from ursina import camera, Vec3, distance import math class TheLostPlanetSim(UniverseSimScenes): def __init__(self): self.sun = Sun(size_scale=5e2).set_resolution(60) # 太阳放大 500 倍 self.sun.glows = (30, 1.005, 0.03) # self.asteroids = Asteroids(size_scale=1.08e2, parent=self.sun) # 小行星模拟(仅 ursina 模拟器支持) # 环状星群带(inner_radius, outer_radius, subdivisions) # inner_radius:内圆半径 outer_radius:外圆半径,subdivisions:细分数,控制圆环的细节和精度 # self.asteroids.torus_zone = 4.7, 5.5, 64 self.moon = Moon(size_scale=3.5e3, init_position=[0, 0, (0.4 + 2.4) * AU], distance_scale=1.76) # ceres = Ceres(size_scale=3e3, distance_scale=1.7) self.earth = Earth(size_scale=3e3, distance_scale=3.8) self.mars = Mars(size_scale=3e3, distance_scale=2.9) self.jupiter = Jupiter(size_scale=0.68e3, distance_scale=1.12) self.saturn = Saturn(size_scale=0.68e3, distance_scale=0.74) self.bodies = [ self.sun, Mercury(size_scale=3e3, distance_scale=7.3), # 水星放大 4000 倍 Venus(size_scale=3e3, distance_scale=4.5), # 金星放大 4000 倍 self.earth, # 地球放大 4000 倍 self.mars, # 火星放大 4000 倍 # asteroids, self.moon, # ceres, # Earth(size_scale=3e3, init_position=[0, 0, (2.17) * AU]), # 地球放大 4000 倍 # Earth(size_scale=3e3, init_position=[0, 0, (3.64) * AU]), # 地球放大 4000 倍 self.jupiter, # 木星放大 680 倍 self.saturn, # 土星放大 680 倍 Uranus(size_scale=0.8e3, distance_scale=0.42), # 天王星放大 800 倍 Neptune(size_scale=1e3, distance_scale=0.3), # 海王星放大 1000 倍 ] # 水星 0.4AU # 金星 0.4+0.3 AU # 地球 0.4+0.6 AU # 火星 0.4+1.2 AU # 木星 0.4+4.8 AU # 土星 0.4+9.6 AU # 天王星 0.4+19.2 AU # d = (n+4)/10 # an = 0.4+0.3×(2n-2) planet_no = -1 for idx, body in enumerate(self.bodies): if isinstance(body, Asteroids): continue body.rotation_speed = 0 planet_no += 1 # body.init_velocity = [0, 0, 0] # an = 0.4 + 0.3 * pow(2,idx) # an = (idx+4)/10 # 其中k=0,1,2,4,8,16,32,64,128 (0以后数字为2的2次方) # 行星 公式推得值 实测值 # 金星 0.7 0.72 # 地球 1 1 # 火星 1.6 1.52 # 谷神星 2.8 2.9 [1] # 木星 5.2 5.2 # 土星 10 9.54 # 天王星 19.6 19.18 # 海王星 38.8 30.06 # 冥王星 77.2 39.44 # 提丢斯-波得定则 # https://baike.baidu.com/item/%E6%8F%90%E4%B8%A2%E6%96%AF-%E6%B3%A2%E5%BE%97%E5%AE%9A%E5%88%99/859407 # 小行星 2.17-3.64天文单位 if planet_no == 0: continue elif planet_no == 1: an = 0.4 else: an = 0.4 + 0.3 * pow(2, planet_no - 2) # print(body.name, an, body.position[2] / AU) self.step_index = 0 self.init_steps() def on_ready(self): """ 事件绑定后,模拟器运行前会触发 @return: """ from ursina import camera, Vec3 # 创建天空 create_sphere_sky(scale=20000, rotation_x=0, rotation_y=80) camera.clip_plane_near = 0.1 camera.clip_plane_far = 1000000 camera.look_at(self.jupiter.planet) # camera.look_at(sun.planet) camera.rotation_z -= 90 # # self.moon.planet.update = lambda :None # def moon_update(): # self.moon.planet.scale = 80 # # self.moon.planet.rotation_y += 10 # # ext_fun_for_method(self.moon.planet, after_run_fun=moon_update) # for i in range(10): # time.sleep(0.1) # create_asteroid() # UniverseSimScenes.show_grid_axises() # self.asteroids.planet.enabled = False self.moon.planet.enabled = False # ceres.planet.enabled = False # self.moon.planet.look_at(self.mars.planet) def set_alpha_animation(self, body, begin_alpha, end_alpha, interval, is_destroy=False): from ursina import destroy if hasattr(body, "planet"): planet = body.planet else: planet = body planet.alpha = begin_alpha if begin_alpha > end_alpha: interval = -abs(interval) else: interval = abs(interval) origin_update = planet.update def alpha_animation(): from ursina import camera, Vec4 origin_update() alpha = planet.alpha alpha += interval if (interval > 0 and alpha >= end_alpha) or (interval < 0 and alpha <= end_alpha): alpha = end_alpha planet.update = origin_update planet.enabled = (alpha > 0) planet.alpha = alpha if is_destroy and not planet.enabled: destroy(planet) # planet.color = Vec4(planet.color[0], planet.color[1], planet.color[2], alpha) # print(body, planet.alpha, planet.rotation_x, planet.rotation_y, planet.rotation_z) planet.update = alpha_animation # ext_fun_for_method(planet, after_run_fun=alpha_animation) # def set_alpha_animation(self, body, begin_alpha, end_alpha, interval, is_destroy=False): # from ursina import destroy # if hasattr(body, "planet"): # planet = body.planet # else: # planet = body # planet.alpha = 0 # # def alpha_animation(): # alpha = planet.alpha # alpha += interval # if alpha > 1: # alpha = 1 # planet.enabled = (alpha > 0) # planet.alpha = alpha # # planet.color = Vec4(planet.color[0], planet.color[1], planet.color[2], alpha) # # print(body, planet.alpha, planet.rotation_x, planet.rotation_y, planet.rotation_z) # # planet.update = alpha_animation # # ext_fun_for_method(planet, after_run_fun=alpha_animation) # def moon_fade_in(self): # """ # 月球渐渐显示 # @return: # """ # # self.moon.planet.look_at(self.mars.planet) # self.moon.planet.alpha = 0 # def alpha_animation(): # from ursina import Vec2,Vec4 # planet = self.moon.planet.main_entity # alpha = planet.alpha # alpha += 0.01 # planet.color = Vec4(planet.color[0], planet.color[1], planet.color[2], alpha) # planet.alpha = alpha # planet.texture_offset = Vec2(0, 0) # planet.texture_scale = Vec2(1, 1) # # self.moon.planet.update = alpha_animation # lambda: None # self.set_alpha_animation(self.moon, 0.0, 1.0, 0.005) # # def moon_fade_in(self): # from ursina import Vec4 # planet = self.moon.planet # planet.alpha = 0 # def update_moon(): # alpha = planet.alpha # alpha += 0.01 # if alpha > 1.0: # alpha = 1.0 # planet.update = lambda: None # planet.color = Vec4(planet.color[0], planet.color[1], planet.color[2], alpha) # planet.alpha = alpha # # planet.update = update_moon # def earth_fade_in(self): # """ # 月球渐渐显示 # @return: # """ # self.earth.planet.look_at(self.mars.planet) # # self.moon.planet.update = lambda: None # self.set_alpha_animation(self.earth, 0.0, 1.0, 0.005) # def moon_fade_out(self): # """ # 月球渐渐显示 # @return: # """ # self.moon.planet.look_at(self.mars.planet) # # self.moon.planet.update = lambda: None # self.set_alpha_animation(self.moon, 1.0, 0.0, 0.005) def create_asteroid(self, init_angle=110, smooth=False): from ursina import Entity, color, Vec3 import math import random pos = self.moon.planet.position # + Vec3() radius = self.moon.planet.position[2] + 50 * random.random() - 15 # * moon.distance_scale asteroid = Entity(model='sphere', position=pos, color=color.white, scale=1.5) asteroid.s_angle = init_angle asteroid.init_pos = pos y = 10 * random.random() - 5 speed = random.random() / 7 + 0.2 def rotation(): angle = math.pi * asteroid.s_angle / 180 x = self.sun.planet.x + radius * math.cos(angle) z = self.sun.planet.z + radius * math.sin(angle) target_pos = Vec3(x, y, z) if asteroid.init_pos is None or not smooth: # angle = math.pi * asteroid.s_angle / 180 # x = self.sun.planet.x + radius * math.cos(angle) # z = self.sun.planet.z + radius * math.sin(angle) asteroid.position = target_pos else: asteroid.look_at(target_pos) asteroid.position += asteroid.forward * 1.5 d = distance(asteroid.position, target_pos) if d < 3: asteroid.init_pos = None asteroid.s_angle += speed asteroid.update = rotation return asteroid def create_asteroids(self): self.asteroids = [] for i in range(400): self.asteroids.append(self.create_asteroid(i)) # def clear_asteroids(self): # for a in self.asteroids: # a.enabled = False def asteroid_fade_in(self): """ 小行星群渐渐显示 @return: """ self.create_asteroids() for a in self.asteroids: self.set_alpha_animation(a, 0.0, 1.0, 0.01) def asteroid_fade_out(self): """ 小行星群渐渐消失 @return: """ for a in self.asteroids: self.set_alpha_animation(a, 1.0, 0.0, 0.01, is_destroy=True) def moon_fade_in(self): """ 月球渐渐显示 @return: """ self.moon.planet.look_at(self.mars.planet) self.moon.planet.update = lambda: None self.set_alpha_animation(self.moon, 0.0, 1.0, 0.005) # def moon_fade_out(self): # """ # 月球渐渐显示 # @return: # """ # self.moon.planet.look_at(self.mars.planet) # # self.moon.planet.update = lambda: None # self.set_alpha_animation(self.moon, 1.0, 0.0, 0.005) def moon_renovation(self): """ 月球改造中 @return: """ if not hasattr(self, "moon_redesign_last_time"): self.moon_redesign_last_time = time.time() self.asteroid_num = 0 c_time = time.time() # set_alpha_animation(asteroids, 0.0, 1.0, 0.01) if c_time - self.moon_redesign_last_time > 0.02: self.moon_redesign_last_time = c_time self.create_asteroid(smooth=True) self.asteroid_num += 1 def moon_move_to_target_and_rotation(self, target, rotation_radius, end_angle, end_tag, forward, angle_val): if hasattr(target, "planet"): target_planet = target.planet.main_entity else: target_planet = target.main_entity if not hasattr(target_planet, "is_forward"): target_planet.is_forward = True if target_planet.is_forward: target_pos = target_planet.position + Vec3(-rotation_radius, 0, 0) self.moon.planet.look_at(target_pos) d = distance(self.moon.planet, target_pos) if d > 3: self.moon.planet.position += self.moon.planet.forward * forward else: target_planet.is_forward = False else: if not hasattr(target_planet, "moon_angle"): setattr(target_planet, "moon_angle", 180) moon_angle = getattr(target_planet, "moon_angle") angle = math.pi * moon_angle / 180 x = target_planet.x + rotation_radius * math.cos(angle) z = target_planet.z + rotation_radius * math.sin(angle) self.moon.planet.position = (x, 0, z) setattr(target_planet, "moon_angle", moon_angle + angle_val) if moon_angle >= end_angle: setattr(self, end_tag, True) self.step_index += 1 # print("moon_angle", moon_angle) def init_steps(self): def step_05(): if not hasattr(self, "moon_around_mars"): # setattr(self, "step_04", True) self.moon_move_to_target_and_rotation(target=self.mars, rotation_radius=12, end_angle=180 + 360 * 3, end_tag="moon_around_mars", forward=0.02, angle_val=0.8) def step_06(): if not hasattr(self, "moon_around_earth"): self.moon_move_to_target_and_rotation(target=self.earth, rotation_radius=15, end_angle=7200, end_tag="moon_around_earth", forward=0.02, angle_val=1) self.steps = [ (self.asteroid_fade_in, 3, 1), (self.asteroid_fade_out, 3, 1), (self.moon_fade_in, 3, 1), (self.moon_renovation, 10, -1), (step_05, -1, -1), (step_06, -1, -1), ] def on_timer_changed(self, time_data): if time_data.years > 1: if self.step_index > len(self.steps) - 1: self.step_index = len(self.steps) - 1 fun, wait_years, run_times = self.steps[self.step_index] if not hasattr(self, f"{fun.__name__}_wait_years"): setattr(self, f"{fun.__name__}_wait_years", time_data.years) setattr(self, f"{fun.__name__}_run_times", 0) fun_run_times = getattr(self, f"{fun.__name__}_run_times") if fun_run_times < run_times or run_times < 0: fun() setattr(self, f"{fun.__name__}_run_times", fun_run_times + 1) fun_wait_years = getattr(self, f"{fun.__name__}_wait_years") if wait_years + fun_wait_years < time_data.years and wait_years > 0: self.step_index += 1 # print(self.step_index, fun.__name__) def on_timer_changed2(self, time_data): from ursina import camera, Vec3, distance import math if time_data.years > 1 and not hasattr(self, "step_01"): # 小行星群渐渐显示,运行一次 self.asteroid_fade_in() setattr(self, "step_01", True) # 控制运行一次 elif time_data.years > 6 and not hasattr(self, "step_02"): # 小行星群渐渐消失,运行一次 self.asteroid_fade_out() setattr(self, "step_02", True) # 控制运行一次 elif time_data.years > 8 and not hasattr(self.moon, "step_03"): # 月球渐渐显示,运行一次 self.moon_fade_in() setattr(self.moon, "step_03", True) # 控制运行一次 elif time_data.years > 10 and not hasattr(self, "step_04"): # 月球改造中,运行多次 self.moon_renovation() if time_data.years > 25 and not hasattr(self, "moon_around_mars"): setattr(self, "step_04", True) self.moon_move_to_target_and_rotation(target=self.mars, rotation_radius=12, end_angle=720, end_tag="moon_around_mars", forward=0.03, angle_val=3) elif hasattr(self, "moon_around_mars") and not hasattr(self, "moon_around_earth"): self.moon_move_to_target_and_rotation(target=self.earth, rotation_radius=15, end_angle=720, end_tag="moon_around_earth", forward=0.03, angle_val=3) if __name__ == '__main__': sim = TheLostPlanetSim() # UniverseSimScenes.set_window_size((1920, 1079), False) # 运行前会触发 on_ready UrsinaEvent.on_ready_subscription(sim.on_ready) # UrsinaEvent.after_ready_subscription(after_ready) UrsinaEvent.on_timer_changed_subscription(sim.on_timer_changed) # 使用 ursina 查看的运行效果 # 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹 # position = 左-右+、上+下-、前+后- ursina_run(sim.bodies, SECONDS_PER_YEAR, gravity_works=False, show_exit_button=False, show_control_info=False, show_camera_info=False, # position=(0, 2 * AU, -11 * AU), # position=(0, 20 * AU, 10 * AU), position=(5.5 * AU, AU, 5 * AU), timer_enabled=True, show_timer=True, cosmic_bg='', # show_trail=True, show_grid=False)