From b231dfabbb748b97f649d9afde851100615cdfe4 Mon Sep 17 00:00:00 2001 From: march3 Date: Wed, 28 Jun 2023 13:13:47 +0800 Subject: [PATCH] =?UTF-8?q?Python=E8=B6=85=E4=BA=BA-=E5=AE=87=E5=AE=99?= =?UTF-8?q?=E6=A8=A1=E6=8B=9F=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bodies/body.py | 8 +- bodies/fixed_stars/alcyone.py | 2 +- bodies/fixed_stars/aldebaran.py | 2 +- bodies/fixed_stars/antares.py | 2 +- bodies/fixed_stars/arcturus.py | 2 +- bodies/fixed_stars/bellatrix.py | 2 +- bodies/fixed_stars/betelgeuse.py | 2 +- bodies/fixed_stars/carinae_v382.py | 2 +- bodies/fixed_stars/eta_carinae.py | 2 +- bodies/fixed_stars/fixed_star.py | 14 +- bodies/fixed_stars/pollux.py | 2 +- bodies/fixed_stars/procyon.py | 2 +- bodies/fixed_stars/rigel.py | 2 +- bodies/fixed_stars/sirius.py | 2 +- bodies/fixed_stars/stephenson_2_18.py | 2 +- bodies/fixed_stars/uy_scuti.py | 2 +- bodies/fixed_stars/vy_canis_majoris.py | 2 +- bodies/fixed_stars/y_canum_venaticorum.py | 2 +- common/func.py | 76 +++++++ sim_scenes/earth/earth_clouds.py | 2 +- sim_scenes/fiction/earth_orbit_stopped.py | 2 +- sim_scenes/fiction/fixed_stars_1.py | 4 +- sim_scenes/fiction/fixed_stars_2.py | 4 +- sim_scenes/func.py | 2 +- sim_scenes/funny/utils/body_utils.py | 4 +- sim_scenes/science/free_fall_of_ball.py | 14 +- sim_scenes/science/jupiter_protects_earth.py | 220 ++++++++++++------- sim_scenes/science/parabolic_curve.py | 16 +- sim_scenes/wonders/comets_jupiter.py | 12 +- simulators/mpl_simulator.py | 4 +- simulators/simulator.py | 4 +- simulators/ursina/entities/planet.py | 2 +- simulators/ursina_simulator.py | 17 +- simulators/views/body_view.py | 6 +- simulators/views/mayavi_view.py | 2 +- 35 files changed, 295 insertions(+), 150 deletions(-) diff --git a/bodies/body.py b/bodies/body.py index 876677a..153dbc3 100644 --- a/bodies/body.py +++ b/bodies/body.py @@ -392,7 +392,7 @@ class Body(metaclass=ABCMeta): return v @property - def raduis(self): + def radius(self): """ 天体的半径(单位:km) @return: @@ -406,11 +406,11 @@ class Body(metaclass=ABCMeta): 天体的直径(单位:km) @return: """ - return self.raduis * 2 + return self.radius * 2 def __repr__(self): return '<%s(%s)> m=%.3e(kg), r|d=%.3e|%.3e(km), v=%.3e(km³), d=%.3e(kg/m³), p=[%.3e,%.3e,%.3e](km), v=%s(km/s)' % \ - (self.name, self.__class__.__name__, self.mass, self.raduis, self.diameter, self.volume, self.density, + (self.name, self.__class__.__name__, self.mass, self.radius, self.diameter, self.volume, self.density, self.position[0], self.position[1], self.position[2], self.velocity) def ignore_gravity_with(self, body): @@ -533,7 +533,7 @@ if __name__ == '__main__': # build_bodies_from_json('../data/sun.json') bodies, params = Body.build_bodies_from_json('../data/sun_earth.json') # 太阳半径 / 地球半径 - print("太阳半径 / 地球半径 =", bodies[0].raduis / bodies[1].raduis) + print("太阳半径 / 地球半径 =", bodies[0].radius / bodies[1].radius) print("params:", params) for body in bodies: print(body) diff --git a/bodies/fixed_stars/alcyone.py b/bodies/fixed_stars/alcyone.py index 26a61cf..9c7c31a 100644 --- a/bodies/fixed_stars/alcyone.py +++ b/bodies/fixed_stars/alcyone.py @@ -98,4 +98,4 @@ if __name__ == '__main__': fixed_star = Alcyone() print(fixed_star) fixed_star.compare_with_sun() - fixed_star.density_by_radius(num_sun_raduis=9.5) + fixed_star.density_by_radius(num_sun_radius=9.5) diff --git a/bodies/fixed_stars/aldebaran.py b/bodies/fixed_stars/aldebaran.py index 2b5b4b2..2c51bd8 100644 --- a/bodies/fixed_stars/aldebaran.py +++ b/bodies/fixed_stars/aldebaran.py @@ -81,4 +81,4 @@ if __name__ == '__main__': fixed_star = Aldebaran() print(fixed_star) fixed_star.compare_with_sun() - fixed_star.density_by_radius(num_sun_raduis=44.13) + fixed_star.density_by_radius(num_sun_radius=44.13) diff --git a/bodies/fixed_stars/antares.py b/bodies/fixed_stars/antares.py index 2325529..5fc9511 100644 --- a/bodies/fixed_stars/antares.py +++ b/bodies/fixed_stars/antares.py @@ -83,4 +83,4 @@ if __name__ == '__main__': fixed_star = Antares() print(fixed_star) fixed_star.compare_with_sun() - fixed_star.density_by_radius(num_sun_raduis=680) + fixed_star.density_by_radius(num_sun_radius=680) diff --git a/bodies/fixed_stars/arcturus.py b/bodies/fixed_stars/arcturus.py index 0dd7e11..f8d7d00 100644 --- a/bodies/fixed_stars/arcturus.py +++ b/bodies/fixed_stars/arcturus.py @@ -75,4 +75,4 @@ if __name__ == '__main__': fixed_star = Arcturus() print(fixed_star) fixed_star.compare_with_sun() - fixed_star.density_by_radius(num_sun_raduis=25.7) + fixed_star.density_by_radius(num_sun_radius=25.7) diff --git a/bodies/fixed_stars/bellatrix.py b/bodies/fixed_stars/bellatrix.py index d2f1d3e..aa50510 100644 --- a/bodies/fixed_stars/bellatrix.py +++ b/bodies/fixed_stars/bellatrix.py @@ -77,4 +77,4 @@ if __name__ == '__main__': fixed_star = Bellatrix() print(fixed_star) fixed_star.compare_with_sun() - fixed_star.density_by_radius(num_sun_raduis=5.75) + fixed_star.density_by_radius(num_sun_radius=5.75) diff --git a/bodies/fixed_stars/betelgeuse.py b/bodies/fixed_stars/betelgeuse.py index 9f91ba6..6b73647 100644 --- a/bodies/fixed_stars/betelgeuse.py +++ b/bodies/fixed_stars/betelgeuse.py @@ -79,4 +79,4 @@ if __name__ == '__main__': fixed_star = Betelgeuse() print(fixed_star) fixed_star.compare_with_sun() - fixed_star.density_by_radius(num_sun_raduis=1180) + fixed_star.density_by_radius(num_sun_radius=1180) diff --git a/bodies/fixed_stars/carinae_v382.py b/bodies/fixed_stars/carinae_v382.py index c9a1530..0709778 100644 --- a/bodies/fixed_stars/carinae_v382.py +++ b/bodies/fixed_stars/carinae_v382.py @@ -78,4 +78,4 @@ if __name__ == '__main__': fixed_star = CarinaeV382() print(fixed_star) fixed_star.compare_with_sun() - fixed_star.density_by_radius(num_sun_raduis=747) + fixed_star.density_by_radius(num_sun_radius=747) diff --git a/bodies/fixed_stars/eta_carinae.py b/bodies/fixed_stars/eta_carinae.py index b52a627..322cdea 100644 --- a/bodies/fixed_stars/eta_carinae.py +++ b/bodies/fixed_stars/eta_carinae.py @@ -72,4 +72,4 @@ if __name__ == '__main__': fixed_star = EtaCarinae() print(fixed_star) fixed_star.compare_with_sun() - fixed_star.density_by_radius(num_sun_raduis=278) + fixed_star.density_by_radius(num_sun_radius=278) diff --git a/bodies/fixed_stars/fixed_star.py b/bodies/fixed_stars/fixed_star.py index 51f3a23..d18ea6e 100644 --- a/bodies/fixed_stars/fixed_star.py +++ b/bodies/fixed_stars/fixed_star.py @@ -97,7 +97,7 @@ class FixedStar(Body): sun = Sun() print("---------------------------------") print("质量: %.2f M☉ (%.4g kg)" % (self.mass / sun.mass, self.mass)) - print("半径: %.2f R☉ (%.4g km)" % (self.raduis / sun.raduis, self.raduis)) + print("半径: %.2f R☉ (%.4g km)" % (self.radius / sun.radius, self.radius)) print("直径: %.2f D☉ (%.4g km)" % (self.diameter / sun.diameter, self.diameter)) num_sun_volume = self.volume / sun.volume # 相当于多少个太阳体积 if num_sun_volume <= 10000: @@ -107,20 +107,20 @@ class FixedStar(Body): else: print("体积: %.2f亿 V☉ (%.4g km³)" % (num_sun_volume / 100000000, self.volume)) - def density_by_radius(self, raduis=None, num_sun_raduis=None): + def density_by_radius(self, radius=None, num_sun_radius=None): """ 密度換算 - @param raduis: 半径的长度(km) - @param num_sun_raduis: 多少个太阳半径 + @param radius: 半径的长度(km) + @param num_sun_radius: 多少个太阳半径 @return: """ from bodies import Sun import math sun = Sun() - if num_sun_raduis is not None: - raduis = num_sun_raduis * sun.raduis - print("---------------------------------\n密度換算: ", self.mass / 1e9 / (4 / 3 * math.pi * pow(raduis, 3))) + if num_sun_radius is not None: + radius = num_sun_radius * sun.radius + print("---------------------------------\n密度換算: ", self.mass / 1e9 / (4 / 3 * math.pi * pow(radius, 3))) if __name__ == '__main__': diff --git a/bodies/fixed_stars/pollux.py b/bodies/fixed_stars/pollux.py index f6611c2..4685c7c 100644 --- a/bodies/fixed_stars/pollux.py +++ b/bodies/fixed_stars/pollux.py @@ -81,4 +81,4 @@ if __name__ == '__main__': fixed_star = Pollux() print(fixed_star) fixed_star.compare_with_sun() - fixed_star.density_by_radius(num_sun_raduis=8.8) + fixed_star.density_by_radius(num_sun_radius=8.8) diff --git a/bodies/fixed_stars/procyon.py b/bodies/fixed_stars/procyon.py index 7fb6c05..e5861a5 100644 --- a/bodies/fixed_stars/procyon.py +++ b/bodies/fixed_stars/procyon.py @@ -99,4 +99,4 @@ if __name__ == '__main__': fixed_star = Procyon() print(fixed_star) fixed_star.compare_with_sun() - fixed_star.density_by_radius(num_sun_raduis=2.05) + fixed_star.density_by_radius(num_sun_radius=2.05) diff --git a/bodies/fixed_stars/rigel.py b/bodies/fixed_stars/rigel.py index 56ed323..e7d04c4 100644 --- a/bodies/fixed_stars/rigel.py +++ b/bodies/fixed_stars/rigel.py @@ -76,4 +76,4 @@ if __name__ == '__main__': fixed_star = Rigel() print(fixed_star) fixed_star.compare_with_sun() - fixed_star.density_by_radius(num_sun_raduis=78) + fixed_star.density_by_radius(num_sun_radius=78) diff --git a/bodies/fixed_stars/sirius.py b/bodies/fixed_stars/sirius.py index b19d519..6196246 100644 --- a/bodies/fixed_stars/sirius.py +++ b/bodies/fixed_stars/sirius.py @@ -101,4 +101,4 @@ if __name__ == '__main__': fixed_star = Sirius() print(fixed_star) fixed_star.compare_with_sun() - fixed_star.density_by_radius(num_sun_raduis=1.71) + fixed_star.density_by_radius(num_sun_radius=1.71) diff --git a/bodies/fixed_stars/stephenson_2_18.py b/bodies/fixed_stars/stephenson_2_18.py index fe134a5..0be47af 100644 --- a/bodies/fixed_stars/stephenson_2_18.py +++ b/bodies/fixed_stars/stephenson_2_18.py @@ -87,4 +87,4 @@ if __name__ == '__main__': fixed_star = Stephenson_2_18() print(fixed_star) fixed_star.compare_with_sun() - fixed_star.density_by_radius(num_sun_raduis=2150) + fixed_star.density_by_radius(num_sun_radius=2150) diff --git a/bodies/fixed_stars/uy_scuti.py b/bodies/fixed_stars/uy_scuti.py index 774eed0..f2ac4c1 100644 --- a/bodies/fixed_stars/uy_scuti.py +++ b/bodies/fixed_stars/uy_scuti.py @@ -75,4 +75,4 @@ if __name__ == '__main__': fixed_star = UYScuti() print(fixed_star) fixed_star.compare_with_sun() - fixed_star.density_by_radius(num_sun_raduis=1708) + fixed_star.density_by_radius(num_sun_radius=1708) diff --git a/bodies/fixed_stars/vy_canis_majoris.py b/bodies/fixed_stars/vy_canis_majoris.py index 433f849..8d70888 100644 --- a/bodies/fixed_stars/vy_canis_majoris.py +++ b/bodies/fixed_stars/vy_canis_majoris.py @@ -78,4 +78,4 @@ if __name__ == '__main__': fixed_star = VYCanisMajoris() print(fixed_star) fixed_star.compare_with_sun() - fixed_star.density_by_radius(num_sun_raduis=1400) + fixed_star.density_by_radius(num_sun_radius=1400) diff --git a/bodies/fixed_stars/y_canum_venaticorum.py b/bodies/fixed_stars/y_canum_venaticorum.py index 8019785..4f38718 100644 --- a/bodies/fixed_stars/y_canum_venaticorum.py +++ b/bodies/fixed_stars/y_canum_venaticorum.py @@ -76,4 +76,4 @@ if __name__ == '__main__': fixed_star = YCanumVenaticorum() print(fixed_star) fixed_star.compare_with_sun() - fixed_star.density_by_radius(num_sun_raduis=215) + fixed_star.density_by_radius(num_sun_radius=215) diff --git a/common/func.py b/common/func.py index 88ada0d..18d6525 100644 --- a/common/func.py +++ b/common/func.py @@ -133,6 +133,82 @@ def get_acceleration_info(acceleration): acc_info = "0 m/s²" return acc_info + +def find_intersection(sphere_center, sphere_radius, line_start_pos, line_end_pos): + """ + 计算线段与球体表面的交点坐标 + 参数: + sphere_center (tuple): 球体中心位置的三维坐标 (x, y, z) + sphere_radius (float): 球体的半径 + line_start_pos (tuple): 线段的起始位置的三维坐标 (x, y, z) + line_end_pos (tuple): 线段的结束位置的三维坐标 (x, y, z) + 返回: + tuple: 交点的三维坐标 (x, y, z),如果没有交点则返回None + """ + # 计算线段的方向向量 + line_direction = ( + line_end_pos[0] - line_start_pos[0], + line_end_pos[1] - line_start_pos[1], + line_end_pos[2] - line_start_pos[2] + ) + # 计算线段的长度 + line_length = math.sqrt( + line_direction[0] ** 2 + + line_direction[1] ** 2 + + line_direction[2] ** 2 + ) + # 将线段方向向量归一化 + line_direction = ( + line_direction[0] / line_length, + line_direction[1] / line_length, + line_direction[2] / line_length + ) + # 计算线段起始位置到球心的向量 + start_to_center = ( + sphere_center[0] - line_start_pos[0], + sphere_center[1] - line_start_pos[1], + sphere_center[2] - line_start_pos[2] + ) + # 计算线段起始位置到球心的距离 + distance = ( + start_to_center[0] * line_direction[0] + + start_to_center[1] * line_direction[1] + + start_to_center[2] * line_direction[2] + ) + # 如果距离小于0,则线段与球体没有交点 + if distance < 0: + return None + # 计算最短距离的投影点坐标 + closest_point = ( + line_start_pos[0] + distance * line_direction[0], + line_start_pos[1] + distance * line_direction[1], + line_start_pos[2] + distance * line_direction[2] + ) + # 计算最短距离的投影点到球心的距离 + closest_distance = math.sqrt( + (closest_point[0] - sphere_center[0]) ** 2 + + (closest_point[1] - sphere_center[1]) ** 2 + + (closest_point[2] - sphere_center[2]) ** 2 + ) + # 如果最短距离大于球体半径,则线段与球体没有交点 + if closest_distance > sphere_radius: + return None + # 计算交点坐标 + intersection = ( + closest_point[0] + (sphere_radius - closest_distance) * line_direction[0], + closest_point[1] + (sphere_radius - closest_distance) * line_direction[1], + closest_point[2] + (sphere_radius - closest_distance) * line_direction[2] + ) + return intersection + + +if __name__ == '__main__': + sphere_center = (0, 0, 0) + sphere_radius = 1 + line_start_pos = (-1, 0, 0) + line_end_pos = (1, 0, 0) + intersection = find_intersection(sphere_center, sphere_radius, line_start_pos, line_end_pos) + print(intersection) # # def calculate_velocity(mass, semimajor_axis, eccentricity): # """ diff --git a/sim_scenes/earth/earth_clouds.py b/sim_scenes/earth/earth_clouds.py index 4233bbc..dad6d3e 100644 --- a/sim_scenes/earth/earth_clouds.py +++ b/sim_scenes/earth/earth_clouds.py @@ -33,6 +33,6 @@ if __name__ == '__main__': # 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹 # position = 左-右+、上+下-、前+后- ursina_run(bodies, SECONDS_PER_HOUR / 2, - position=(1.5 * earth.raduis, 0, -30000), + position=(1.5 * earth.radius, 0, -30000), show_grid=False, view_closely=0.001) # 近距离观看 view_closely=True或0.001 diff --git a/sim_scenes/fiction/earth_orbit_stopped.py b/sim_scenes/fiction/earth_orbit_stopped.py index d268fcf..c7fa594 100644 --- a/sim_scenes/fiction/earth_orbit_stopped.py +++ b/sim_scenes/fiction/earth_orbit_stopped.py @@ -110,7 +110,7 @@ class EarthOrbitStoppedSim: # 计算地球和太阳中心点之间的距离 distance = calculate_distance(self.earth.position, self.sun.position) # 减去太阳和地球的半径,得到地球表面和太阳表面的距离 - distance = distance - self.sun.raduis - self.earth.raduis + distance = distance - self.sun.radius - self.earth.radius if distance > 100000000: distance_str = "%s亿" % round(distance / 100000000.0, 2) diff --git a/sim_scenes/fiction/fixed_stars_1.py b/sim_scenes/fiction/fixed_stars_1.py index 2aca125..7a0475d 100644 --- a/sim_scenes/fiction/fixed_stars_1.py +++ b/sim_scenes/fiction/fixed_stars_1.py @@ -48,11 +48,11 @@ if __name__ == '__main__': if idx == 0: # 这是地球 d = 0 else: - d = pow((body.raduis + bodies[idx - 1].raduis) * SIZE_SCALE, 1.0) * 1.1 + d = pow((body.radius + bodies[idx - 1].radius) * SIZE_SCALE, 1.0) * 1.1 # 所有天体的初始速度为 0 body.init_velocity = [0, 0, 0] # 所有天体的初始位置进行赋值 - body.init_position = [-(distance_sum + d), AU, body.raduis * SIZE_SCALE] + body.init_position = [-(distance_sum + d), AU, body.radius * SIZE_SCALE] distance_sum += d # 使用 ursina 查看的运行效果 diff --git a/sim_scenes/fiction/fixed_stars_2.py b/sim_scenes/fiction/fixed_stars_2.py index 0e6d002..40e902f 100644 --- a/sim_scenes/fiction/fixed_stars_2.py +++ b/sim_scenes/fiction/fixed_stars_2.py @@ -48,11 +48,11 @@ if __name__ == '__main__': if idx == 0: # 这是地球 d = 0 else: - d = pow((body.raduis + bodies[idx - 1].raduis) * SIZE_SCALE, 1.0) * 1.1 + d = pow((body.radius + bodies[idx - 1].radius) * SIZE_SCALE, 1.0) * 1.1 # 所有天体的初始速度为 0 body.init_velocity = [0, 0, 0] # 所有天体的初始位置进行赋值 - body.init_position = [-body.raduis * SIZE_SCALE / 1.1, body.raduis * SIZE_SCALE / 1.1, d] + body.init_position = [-body.radius * SIZE_SCALE / 1.1, body.radius * SIZE_SCALE / 1.1, d] distance_sum += d # 使用 ursina 查看的运行效果 diff --git a/sim_scenes/func.py b/sim_scenes/func.py index c445fb6..27a3526 100644 --- a/sim_scenes/func.py +++ b/sim_scenes/func.py @@ -342,7 +342,7 @@ def two_bodies_colliding(body1: Body, body2: Body): d = calculate_distance(np.array(body1.position) * body1.distance_scale, np.array(body2.position) * body2.distance_scale) - if d <= body1.raduis * body1.size_scale + body2.raduis * body2.size_scale: + if d <= body1.radius * body1.size_scale + body2.radius * body2.size_scale: return True return False diff --git a/sim_scenes/funny/utils/body_utils.py b/sim_scenes/funny/utils/body_utils.py index caceae2..c2ae9db 100644 --- a/sim_scenes/funny/utils/body_utils.py +++ b/sim_scenes/funny/utils/body_utils.py @@ -46,8 +46,8 @@ def gen_bodies_from_image(pixel_image, params, texture="color_body.png"): # return get_scaled_body_pos((camera_pos[2], camera_pos[1], camera_pos[0]), pos, scale) return get_scaled_body_pos((camera_pos[0], camera_pos[1], camera_pos[2]), pos, scale) - # # body.init_position = [body.raduis * SIZE_SCALE, (distance_sum + d), AU] - # body.init_position = [-(distance_sum + d), AU, body.raduis * SIZE_SCALE] + # # body.init_position = [body.radius * SIZE_SCALE, (distance_sum + d), AU] + # body.init_position = [-(distance_sum + d), AU, body.radius * SIZE_SCALE] # # [ 远+近- , 左+右- , 上+下-] # return pos[0] + (scale - 1.0) * 300 * (random.randint(90, 110)) * D, pos[1], pos[2] diff --git a/sim_scenes/science/free_fall_of_ball.py b/sim_scenes/science/free_fall_of_ball.py index d0d7a0b..4ceadbd 100644 --- a/sim_scenes/science/free_fall_of_ball.py +++ b/sim_scenes/science/free_fall_of_ball.py @@ -18,21 +18,21 @@ if __name__ == '__main__': # 创建一个地球,位于中心位置,初始速度为 0 earth = Earth(init_position=[0, 0, 0], init_velocity=[0, 0, 0], size_scale=1, texture="earth_hd.jpg") - # 地球的半径 = earth.raduis = 6373.22 + # 地球的半径 = earth.radius = 6373.22 # 创建的3个不同质量,不同高度的球,观察这3个球打到地球表面上的加速度 ball_1 = Football(mass=500, size_scale=2.65e2, trail_color=[255, 0, 0], # 球在地面上 518 多公里处 - # 518 = sqrt[(earth.raduis + 500)² + (-500)²] - earth.raduis - init_position=[-500, earth.raduis + 500, 0], + # 518 = sqrt[(earth.radius + 500)² + (-500)²] - earth.radius + init_position=[-500, earth.radius + 500, 0], init_velocity=[0, 0, 0], gravity_only_for=[earth]) ball_2 = Football(mass=1000, size_scale=3.3e2, trail_color=[0, 255, 0], # 球在地面上 800 多公里处 - init_position=[0, earth.raduis + 800, 0], + init_position=[0, earth.radius + 800, 0], init_velocity=[0, 0, 0], gravity_only_for=[earth]) ball_3 = Football(mass=5000, size_scale=3.8e2, trail_color=[0, 0, 255], # 球在地面上 1016 多公里处 - # 1016 = sqrt[(earth.raduis + 1000)² + 500²] - earth.raduis - init_position=[500, earth.raduis + 1000, 0], + # 1016 = sqrt[(earth.radius + 1000)² + 500²] - earth.radius + init_position=[500, earth.radius + 1000, 0], init_velocity=[0, 0, 0], gravity_only_for=[earth]) bodies = [earth, ball_1, ball_2, ball_3] @@ -41,7 +41,7 @@ if __name__ == '__main__': # 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹 # position = 左-右+、上+下-、前+后- ursina_run(bodies, SECONDS_PER_MINUTE, # 一秒相当于一分钟 - position=(0, earth.raduis + 500, -4500), + position=(0, earth.radius + 500, -4500), show_trail=True, show_timer=True, view_closely=0.001) diff --git a/sim_scenes/science/jupiter_protects_earth.py b/sim_scenes/science/jupiter_protects_earth.py index 39769a4..69bf72a 100644 --- a/sim_scenes/science/jupiter_protects_earth.py +++ b/sim_scenes/science/jupiter_protects_earth.py @@ -9,117 +9,173 @@ from bodies import Sun, Jupiter, Earth from objs import RockSnow, Rock, create_rock from common.consts import SECONDS_PER_MONTH, SECONDS_PER_YEAR -from sim_scenes.func import ursina_run, camera_look_at, two_bodies_colliding -from simulators.ursina.entities.body_timer import TimeData +from sim_scenes.func import ursina_run, camera_look_at, two_bodies_colliding, create_text_panel +from simulators.ursina.entities.body_timer import TimeData, BodyTimer from simulators.ursina.entities.entity_utils import create_directional_light from simulators.ursina.ursina_event import UrsinaEvent import random +import math -def create_comet(index, raduis, gravity_only_for): - """ - 随机生成石头(随机位置、随机初始速度、随机大小、随机旋转) - @param index: 索引号 - @param raduis: 木星的半径,保证生成的石头在木星半径外 - @param gravity_only_for: 指定一个天体,石头只对该天体引力有效 - @return: - """ - # 随机生成石头的位置和初始速度信息 - pos = [-raduis * random.randint(120, 200) / 100, - -raduis * random.randint(120, 200) / 1000, - -raduis * random.randint(100, 300) / 100] - # 随机速度 - vel = [-random.randint(90, 200) / 300, 0, 0] - # vel = [0, 0, 0] - # 石头随机大小 - size_scale = random.randint(600, 1200) * 2e4 - # 随机创建石头 - rock = create_rock( - no=index % 8 + 1, name=f'岩石{index + 1}', mass=4.4e10, - size_scale=size_scale, color=(255, 200, 0), - init_position=pos, init_velocity=vel, gravity_only_for=gravity_only_for - ) - - # 给石头一个随机旋转的方向和值 - rock.rotation = [0, 0, 0] - rock.rotation[random.randint(0, 2)] = random.randint(90, 200) / 100 - return rock - - -colliding_count = [0, 0, 0] +class JupiterProtectsEarthSim: -if __name__ == '__main__': - """ - 彗木保护地球模拟 - """ - sun = Sun(name="太阳", size_scale=0.8e2) # 太阳放大 80 倍,距离保持不变 - jupiter = Jupiter(name="木星", size_scale=0.6e3) # 木星放大 600 倍,距离保持不变 - earth = Earth(name="地球", size_scale=2e3) # 地球放大 2000 倍,距离保持不变 - bodies = [sun, jupiter, earth] + def __init__(self, comet_num=20): + """ - for body in bodies: - body.rotation_speed /= 10 # 自转速度降低10倍 + @param comet_num: 随机生成 comet_num 个石头 + """ + self.colliding_count = [0, 0, 0, 0] # 分别保存太阳碰撞次数、木星碰撞次数、地球碰撞次数、木星保护次数 + self.sun = Sun(name="太阳", size_scale=0.8e2) # 太阳放大 80 倍,距离保持不变 + self.jupiter = Jupiter(name="木星", size_scale=0.6e3) # 木星放大 600 倍,距离保持不变 + self.earth = Earth(name="地球", size_scale=2e3) # 地球放大 2000 倍,距离保持不变 + self.bodies = [self.sun, self.jupiter, self.earth] + + for body in self.bodies: + body.rotation_speed /= 10 # 自转速度降低10倍 + + self.comets = [] + + self.comet_radius = self.jupiter.position[2] * 2 - comets = [] + for i in range(comet_num): + # 随机生成 comet_num 个石头 + comet = self.create_comet(i, self.comet_radius, gravity_only_for=[self.sun, self.jupiter, self.earth]) + self.bodies.append(comet) + self.comets.append(comet) - for i in range(50): - # 随机生成50石头 - comet = create_comet(i, jupiter.position[2] * 2, gravity_only_for=[sun, jupiter, earth]) - bodies.append(comet) - comets.append(comet) + # 显示板信息模板 + self.colliding_info = "太阳碰撞:%s次(%s)\n\n木星碰撞:%s次(%s)\n\n地球碰撞:%s次(%s)\n\n木星保护:%s次\n\n" + def random_pos_vel(self, radius): + # 随机生成石头的位置和初始速度信息 + # pos = [-radius * random.randint(120, 200) / 100, + # -radius * random.randint(120, 200) / 1000, + # -radius * random.randint(100, 300) / 100] - def on_timer_changed(time_data: TimeData): + x = radius * math.cos(random.uniform(0, 2 * math.pi)) * random.randint(80, 150) / 100 + z = radius * math.sin(random.uniform(0, 2 * math.pi)) * random.randint(80, 150) / 100 + pos = [x, 0, z] + + # 随机速度 + vel = [-random.randint(90, 200) / 300, 0, 0] + return pos, vel + + def create_comet(self, index, radius, gravity_only_for): + """ + 随机生成石头(随机位置、随机初始速度、随机大小、随机旋转) + @param index: 索引号 + @param radius: 木星的半径,保证生成的石头在木星半径外 + @param gravity_only_for: 指定一个天体,石头只对该天体引力有效 + @return: + """ + # 随机生成石头的位置和初始速度信息 + pos, vel = self.random_pos_vel(radius) + + # vel = [0, 0, 0] + # 石头随机大小 + size_scale = random.randint(600, 1200) * 1.5e4 + # 随机创建石头 + rock = create_rock( + no=index % 8 + 1, name=f'岩石{index + 1}', mass=size_scale / 1000, + size_scale=size_scale, color=(255, 200, 0), + init_position=pos, init_velocity=vel, gravity_only_for=gravity_only_for + ) + + # 给石头一个随机旋转的方向和值 + rock.rotation = [0, 0, 0] + rock.rotation[random.randint(0, 2)] = random.randint(90, 200) / 100 + return rock + + def on_timer_changed(self, time_data: TimeData): # 运行中,每时每刻都会触发 - for comet in comets: + for comet in self.comets: if comet.planet.enabled: + collided = False # 如果是否可见,则旋转石头 comet.planet.rotation += comet.rotation # 循环判断每个石头与木星是否相碰撞,如果相碰撞就爆炸 - if two_bodies_colliding(comet, jupiter): + if two_bodies_colliding(comet, self.jupiter): # 将石头隐藏、设置引力无效后,展示爆炸效果 - comet.explode(jupiter) - colliding_count[1] += 1 - elif two_bodies_colliding(comet, sun): + # comet.explode(self.jupiter) + # 碰撞到木星,该石头不要爆炸,尝试看看是否会碰撞到地球,如果碰撞了地球,则“木星保护”统计加一 + self.colliding_count[1] += 1 + # 加入标记,说明该石头已经碰撞到木星 + setattr(comet, "jupiter_collided", True) + # collided = True + elif two_bodies_colliding(comet, self.sun): # 将石头隐藏、设置引力无效后,展示爆炸效果 - comet.explode(sun) - colliding_count[0] += 1 - elif two_bodies_colliding(comet, earth): + comet.explode(self.sun) + if not hasattr(comet, "jupiter_collided"): + # 该石头没有碰撞到木星,才算一次 + self.colliding_count[0] += 1 + collided = True + elif two_bodies_colliding(comet, self.earth): + if hasattr(comet, "jupiter_collided"): + # 说明该石头已经碰撞到木星,也就是说木星保护了地球一次 + self.colliding_count[3] += 1 + else: + # 该石头没有碰撞到木星,才算一次 + self.colliding_count[2] += 1 + # 将石头隐藏、设置引力无效后,展示爆炸效果 - comet.explode(earth) - colliding_count[2] += 1 + comet.explode(self.earth) + collided = True + if collided: + # 如果碰撞了,则该石头重复再利用,这样才保证有无限个石头可用 + pos, vel = self.random_pos_vel(self.comet_radius) + comet.init_position = pos + comet.init_velocity = vel + comet.set_visible(True) + comet.ignore_mass = False + comet.planet.enabled = True + if hasattr(comet, "jupiter_collided"): + delattr(comet, "jupiter_collided") + + print("Sun:%s Jupiter:%s Earth:%s" % (self.colliding_count[0], + self.colliding_count[1], + self.colliding_count[2])) + sun_cnt, jupiter_cnt, earth_cnt, protected_cnt = self.colliding_count + total_cnt = sun_cnt + jupiter_cnt + earth_cnt + if total_cnt == 0: + colliding_info = self.colliding_info % (0, "0.0%", + 0, "0.0%", + 0, "0.0%", + 0) + else: + colliding_info = self.colliding_info % (sun_cnt, str(round(sun_cnt * 100 / total_cnt, 2)) + "%", + jupiter_cnt, str(round(jupiter_cnt * 100 / total_cnt, 2)) + "%", + earth_cnt, str(round(earth_cnt * 100 / total_cnt, 2)) + "%", + protected_cnt) + + self.text_panel.text = colliding_info + + def on_ready(self): + # 运行前触发 + self.text_panel = create_text_panel() + self.text_panel.text = self.colliding_info % (0, "0.0%", + 0, "0.0%", + 0, "0.0%", + 0) - print("Sun:%s Jupiter:%s Earth:%s" % (colliding_count[0], colliding_count[1], colliding_count[2])) - - # def on_reset(): - """ - 当按键盘的 “O” 键重置后,恢复所有石头的状态(石头可见、引力有效),这样就可以反复观看 - @return: - """ - # for comet in comets: - # comet.set_visible(True) - # comet.ignore_mass = False +if __name__ == '__main__': + """ + 彗木保护地球模拟 + """ - def on_ready(): - # 运行前触发 - # 为了较好的立体效果,可以增加太阳光线,光线指向木星(target=jupiter) - create_directional_light(position=(200, 0, -300), light_num=3, target=jupiter) - # 摄像机看向木星 - camera_look_at(jupiter, rotation_z=0) + # 设置计时器的最小时间单位为年 + BodyTimer().min_unit = BodyTimer.MIN_UNIT_YEARS + sim = JupiterProtectsEarthSim(comet_num=30) - # 订阅事件后,上面3个函数功能才会起作用 - # 按键盘的 “O” 重置键会触发 on_reset - # UrsinaEvent.on_reset_subscription(on_reset) # 运行前会触发 on_ready - # UrsinaEvent.on_ready_subscription(on_ready) + UrsinaEvent.on_ready_subscription(sim.on_ready) # 运行中,每时每刻都会触发 on_timer_changed - UrsinaEvent.on_timer_changed_subscription(on_timer_changed) + UrsinaEvent.on_timer_changed_subscription(sim.on_timer_changed) # 使用 ursina 查看的运行效果 # 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹 # position = 左-右+、上+下-、前+后- - ursina_run(bodies, SECONDS_PER_YEAR, + ursina_run(sim.bodies, SECONDS_PER_MONTH * 3, position=(30000000, 0, -3000000000), show_timer=True) diff --git a/sim_scenes/science/parabolic_curve.py b/sim_scenes/science/parabolic_curve.py index 214f396..1947277 100644 --- a/sim_scenes/science/parabolic_curve.py +++ b/sim_scenes/science/parabolic_curve.py @@ -14,11 +14,11 @@ from simulators.ursina.entities.body_timer import TimeData from simulators.ursina.ursina_event import UrsinaEvent -def create_ejected_object(velocity, raduis, trail_color, gravity_only_for, angle=10): +def create_ejected_object(velocity, radius, trail_color, gravity_only_for, angle=10): """ 创建一个抛出物体 @param velocity: 抛出去的速度 - @param raduis: 地球中心的半径 + @param radius: 地球中心的半径 @param trail_color: 轨迹颜色 @param gravity_only_for: 指定一个天体,被抛的物体的引力只对该天体有效 @param angle: 抛出去的角度(地平线夹角,默认为10) @@ -27,7 +27,7 @@ def create_ejected_object(velocity, raduis, trail_color, gravity_only_for, angle # 根据速度、角度获取矢量速度(vx、vy) -> vx² + vy² = velocity² vx, vy = get_vector2d_velocity(velocity, angle=angle) football = Football(name=f'物体速度:{velocity}', mass=500, size_scale=1e3, - trail_color=trail_color, init_position=[0, raduis + 500, 0], + trail_color=trail_color, init_position=[0, radius + 500, 0], init_velocity=[vx, vy, 0], gravity_only_for=[gravity_only_for]) return football @@ -39,19 +39,19 @@ if __name__ == '__main__': # 地球在中心位置 earth = Earth(init_position=[0, 0, 0], init_velocity=[0, 0, 0], size_scale=1, rotation_speed=0, texture="earth_hd.jpg") - raduis = earth.raduis + radius = earth.radius # 创建4个不同的抛出速度的物体,速度分别为:7.5km/s、8.5km/s、10km/s、11.2km/s(第二宇宙速度) # velocity = 7.5,飞不出地球太远,就落地(仅适用于地球的重力,物体之间重力不要受到影响) - obj0 = create_ejected_object(velocity=7.5, raduis=raduis, trail_color=(255, 0, 255), # 粉色 + obj0 = create_ejected_object(velocity=7.5, radius=radius, trail_color=(255, 0, 255), # 粉色 gravity_only_for=earth) # 被抛物只对该地球引力有效 # velocity = 8.5,飞不出地球太远,就落地 - obj1 = create_ejected_object(velocity=8.5, raduis=raduis, trail_color=(255, 0, 0), # 红色 + obj1 = create_ejected_object(velocity=8.5, radius=radius, trail_color=(255, 0, 0), # 红色 gravity_only_for=earth) # 被抛物只对该地球引力有效 # velocity = 10,能飞出地球很远,但还是无法摆脱地球引力 - obj2 = create_ejected_object(velocity=10, raduis=raduis, trail_color=(0, 255, 0), # 绿色 + obj2 = create_ejected_object(velocity=10, radius=radius, trail_color=(0, 255, 0), # 绿色 gravity_only_for=earth) # 被抛物只对该地球引力有效 # velocity = 11.2,脱离地球引力直接飞出。速度11.2千米/秒为脱离地球引力的速度叫第二宇宙速度 - obj3 = create_ejected_object(velocity=11.2, raduis=raduis, trail_color=(0, 0, 255), # 蓝色 + obj3 = create_ejected_object(velocity=11.2, radius=radius, trail_color=(0, 0, 255), # 蓝色 gravity_only_for=earth) # 被抛物只对该地球引力有效 bodies = [earth, obj0, obj1, obj2, obj3] diff --git a/sim_scenes/wonders/comets_jupiter.py b/sim_scenes/wonders/comets_jupiter.py index 504d9be..b07f97a 100644 --- a/sim_scenes/wonders/comets_jupiter.py +++ b/sim_scenes/wonders/comets_jupiter.py @@ -16,18 +16,18 @@ from simulators.ursina.ursina_event import UrsinaEvent import random -def create_comet(index, raduis, gravity_only_for): +def create_comet(index, radius, gravity_only_for): """ 随机生成石头(随机位置、随机初始速度、随机大小、随机旋转) @param index: 索引号 - @param raduis: 木星的半径,保证生成的石头在木星半径外 + @param radius: 木星的半径,保证生成的石头在木星半径外 @param gravity_only_for: 指定一个天体,石头只对该天体引力有效 @return: """ # 随机生成石头的位置和初始速度信息 - pos = [-raduis * random.randint(120, 200) / 100, - -raduis * random.randint(120, 200) / 1000, - -raduis * random.randint(100, 300) / 100] + pos = [-radius * random.randint(120, 200) / 100, + -radius * random.randint(120, 200) / 1000, + -radius * random.randint(100, 300) / 100] # 随机速度 vel = [0, -random.randint(90, 200) / 30, 0] # 石头随机大小 @@ -58,7 +58,7 @@ if __name__ == '__main__': for i in range(20): # 随机生成石头 - comet = create_comet(i, jupiter.raduis, gravity_only_for=jupiter) + comet = create_comet(i, jupiter.radius, gravity_only_for=jupiter) bodies.append(comet) comets.append(comet) diff --git a/simulators/mpl_simulator.py b/simulators/mpl_simulator.py index 752a14f..aa74b9c 100644 --- a/simulators/mpl_simulator.py +++ b/simulators/mpl_simulator.py @@ -103,8 +103,8 @@ class MplSimulator(Simulator): else: color = 'blue' # size = 800 if str(body.name).lower().startswith("sun") else 500 - size = body.raduis * body.size_scale / 80000 - # size = pow(body.raduis / AU * body.size_scale,3) + size = body.radius * body.size_scale / 80000 + # size = pow(body.radius / AU * body.size_scale,3) pos = body.position / AU # 天体 diff --git a/simulators/simulator.py b/simulators/simulator.py index 13f3f84..1765c5f 100644 --- a/simulators/simulator.py +++ b/simulators/simulator.py @@ -55,8 +55,8 @@ class Simulator(metaclass=ABCMeta): view.acceleration = body.acceleration view.velocity = body.velocity # viewer.volume = body.volume - if hasattr(body, "raduis"): - view.raduis = body.raduis + if hasattr(body, "radius"): + view.radius = body.radius view.his_position = body.his_position() if hasattr(body, "is_fixed_star"): view.is_fixed_star = body.is_fixed_star diff --git a/simulators/ursina/entities/planet.py b/simulators/ursina/entities/planet.py index 9fc45da..46778f3 100644 --- a/simulators/ursina/entities/planet.py +++ b/simulators/ursina/entities/planet.py @@ -223,7 +223,7 @@ class Planet(Entity): if self.rotation_speed is None or dt == 0: self.rotspeed = 0 # 旋转速度和大小成反比(未使用真实数据) - # self.rotspeed = 30000 / self.body_view.raduis # random.uniform(1.0, 2.0) + # self.rotspeed = 30000 / self.body_view.radius # random.uniform(1.0, 2.0) else: # 是通过月球保持一面面对地球,调整得到 self.rotspeed = self.rotation_speed * (dt / 3600) / 2.4 * \ diff --git a/simulators/ursina_simulator.py b/simulators/ursina_simulator.py index 6952078..3ee7156 100644 --- a/simulators/ursina_simulator.py +++ b/simulators/ursina_simulator.py @@ -23,7 +23,7 @@ from simulators.simulator import Simulator from common.system import System from simulators.ursina.entities.world_grid import WorldGrid from simulators.ursina.entities.sphere_sky import SphereSky -from common.func import find_file +from common.func import find_file, find_intersection import datetime import os from ursina import EditorCamera @@ -92,6 +92,19 @@ class UrsinaSimulator(Simulator): def body_explode(target=None): # from panda3d.core import GeomUtils if body.planet.enabled: + # TODO:下面代码保留,由于运行太快导致两个天体不是在表面碰撞,这样就要进行计算,希望在表面爆炸,但是需要耗费CPU资源,暂时注释 + # line_start_pos = body.his_position()[0] * UrsinaConfig.SCALE_FACTOR + # line_end_pos = body.planet.position + # sphere_center = target.position * UrsinaConfig.SCALE_FACTOR + # # sphere_radius = target.planet.scale_x/2 + # sphere_radius = target.radius * target.size_scale * UrsinaConfig.SCALE_FACTOR + # explode_pos = find_intersection(sphere_center, sphere_radius, line_start_pos, line_end_pos) + # if explode_pos is None: + # explode_pos = body.planet.position + # else: + # print("explode_pos", explode_pos) + + explode_pos = body.planet.position # 如果爆炸,则静止不动(停止并忽略引力) body.stop_and_ignore_gravity() body.planet.enabled = False @@ -103,7 +116,7 @@ class UrsinaSimulator(Simulator): scale = 3 * volume_scale * body.size_scale * UrsinaConfig.SCALE_FACTOR print(scale, body) explode_ani = Animation(explosion_file, - position=body.planet.position, + position=explode_pos, scale=scale, fps=6, loop=False, autoplay=True) explode_ani.set_light_off() diff --git a/simulators/views/body_view.py b/simulators/views/body_view.py index 21ee055..6790ee7 100644 --- a/simulators/views/body_view.py +++ b/simulators/views/body_view.py @@ -39,8 +39,8 @@ class BodyView(metaclass=ABCMeta): self.name = body.name self.mass = body.mass - if hasattr(body, "raduis"): - self.raduis = body.raduis + if hasattr(body, "radius"): + self.radius = body.radius self.velocity = body.velocity @@ -48,7 +48,7 @@ class BodyView(metaclass=ABCMeta): def __repr__(self): return '<%s> m=%.3e(kg), r=%.3e(km), p=[%.3e,%.3e,%.3e](km), v=%s(km/s)' % \ - (self.name, self.mass, self.raduis, + (self.name, self.mass, self.radius, self.position[0], self.position[1], self.position[2], self.velocity) def __find_texture(self, texture): diff --git a/simulators/views/mayavi_view.py b/simulators/views/mayavi_view.py index bfa1d04..b8f0acf 100644 --- a/simulators/views/mayavi_view.py +++ b/simulators/views/mayavi_view.py @@ -70,7 +70,7 @@ class MayaviView(BodyView): self.body.size_scale * rings_scale + \ self.body.position[1] # * self.body.distance_scale # 带环的厚度 - thicknesses_scale = self.body.raduis * 20 + thicknesses_scale = self.body.radius * 20 torus[2][i][j] = thicknesses_scale * np.sin(phi[j]) + \ self.body.position[2] # * self.body.distance_scale rings_color = (173 / 255, 121 / 255, 92 / 255) -- GitLab