提交 b231dfab 编写于 作者: 三月三net's avatar 三月三net

Python超人-宇宙模拟器

上级 c8386bbc
...@@ -392,7 +392,7 @@ class Body(metaclass=ABCMeta): ...@@ -392,7 +392,7 @@ class Body(metaclass=ABCMeta):
return v return v
@property @property
def raduis(self): def radius(self):
""" """
天体的半径(单位:km) 天体的半径(单位:km)
@return: @return:
...@@ -406,11 +406,11 @@ class Body(metaclass=ABCMeta): ...@@ -406,11 +406,11 @@ class Body(metaclass=ABCMeta):
天体的直径(单位:km) 天体的直径(单位:km)
@return: @return:
""" """
return self.raduis * 2 return self.radius * 2
def __repr__(self): 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)' % \ 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) self.position[0], self.position[1], self.position[2], self.velocity)
def ignore_gravity_with(self, body): def ignore_gravity_with(self, body):
...@@ -533,7 +533,7 @@ if __name__ == '__main__': ...@@ -533,7 +533,7 @@ if __name__ == '__main__':
# build_bodies_from_json('../data/sun.json') # build_bodies_from_json('../data/sun.json')
bodies, params = Body.build_bodies_from_json('../data/sun_earth.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) print("params:", params)
for body in bodies: for body in bodies:
print(body) print(body)
...@@ -98,4 +98,4 @@ if __name__ == '__main__': ...@@ -98,4 +98,4 @@ if __name__ == '__main__':
fixed_star = Alcyone() fixed_star = Alcyone()
print(fixed_star) print(fixed_star)
fixed_star.compare_with_sun() 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)
...@@ -81,4 +81,4 @@ if __name__ == '__main__': ...@@ -81,4 +81,4 @@ if __name__ == '__main__':
fixed_star = Aldebaran() fixed_star = Aldebaran()
print(fixed_star) print(fixed_star)
fixed_star.compare_with_sun() 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)
...@@ -83,4 +83,4 @@ if __name__ == '__main__': ...@@ -83,4 +83,4 @@ if __name__ == '__main__':
fixed_star = Antares() fixed_star = Antares()
print(fixed_star) print(fixed_star)
fixed_star.compare_with_sun() fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=680) fixed_star.density_by_radius(num_sun_radius=680)
...@@ -75,4 +75,4 @@ if __name__ == '__main__': ...@@ -75,4 +75,4 @@ if __name__ == '__main__':
fixed_star = Arcturus() fixed_star = Arcturus()
print(fixed_star) print(fixed_star)
fixed_star.compare_with_sun() 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)
...@@ -77,4 +77,4 @@ if __name__ == '__main__': ...@@ -77,4 +77,4 @@ if __name__ == '__main__':
fixed_star = Bellatrix() fixed_star = Bellatrix()
print(fixed_star) print(fixed_star)
fixed_star.compare_with_sun() 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)
...@@ -79,4 +79,4 @@ if __name__ == '__main__': ...@@ -79,4 +79,4 @@ if __name__ == '__main__':
fixed_star = Betelgeuse() fixed_star = Betelgeuse()
print(fixed_star) print(fixed_star)
fixed_star.compare_with_sun() fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=1180) fixed_star.density_by_radius(num_sun_radius=1180)
...@@ -78,4 +78,4 @@ if __name__ == '__main__': ...@@ -78,4 +78,4 @@ if __name__ == '__main__':
fixed_star = CarinaeV382() fixed_star = CarinaeV382()
print(fixed_star) print(fixed_star)
fixed_star.compare_with_sun() fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=747) fixed_star.density_by_radius(num_sun_radius=747)
...@@ -72,4 +72,4 @@ if __name__ == '__main__': ...@@ -72,4 +72,4 @@ if __name__ == '__main__':
fixed_star = EtaCarinae() fixed_star = EtaCarinae()
print(fixed_star) print(fixed_star)
fixed_star.compare_with_sun() fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=278) fixed_star.density_by_radius(num_sun_radius=278)
...@@ -97,7 +97,7 @@ class FixedStar(Body): ...@@ -97,7 +97,7 @@ class FixedStar(Body):
sun = Sun() sun = Sun()
print("---------------------------------") print("---------------------------------")
print("质量: %.2f M☉ (%.4g kg)" % (self.mass / sun.mass, self.mass)) 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)) print("直径: %.2f D☉ (%.4g km)" % (self.diameter / sun.diameter, self.diameter))
num_sun_volume = self.volume / sun.volume # 相当于多少个太阳体积 num_sun_volume = self.volume / sun.volume # 相当于多少个太阳体积
if num_sun_volume <= 10000: if num_sun_volume <= 10000:
...@@ -107,20 +107,20 @@ class FixedStar(Body): ...@@ -107,20 +107,20 @@ class FixedStar(Body):
else: else:
print("体积: %.2f亿 V☉ (%.4g km³)" % (num_sun_volume / 100000000, self.volume)) 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 radius: 半径的长度(km)
@param num_sun_raduis: 多少个太阳半径 @param num_sun_radius: 多少个太阳半径
@return: @return:
""" """
from bodies import Sun from bodies import Sun
import math import math
sun = Sun() sun = Sun()
if num_sun_raduis is not None: if num_sun_radius is not None:
raduis = num_sun_raduis * sun.raduis radius = num_sun_radius * sun.radius
print("---------------------------------\n密度換算: ", self.mass / 1e9 / (4 / 3 * math.pi * pow(raduis, 3))) print("---------------------------------\n密度換算: ", self.mass / 1e9 / (4 / 3 * math.pi * pow(radius, 3)))
if __name__ == '__main__': if __name__ == '__main__':
......
...@@ -81,4 +81,4 @@ if __name__ == '__main__': ...@@ -81,4 +81,4 @@ if __name__ == '__main__':
fixed_star = Pollux() fixed_star = Pollux()
print(fixed_star) print(fixed_star)
fixed_star.compare_with_sun() 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)
...@@ -99,4 +99,4 @@ if __name__ == '__main__': ...@@ -99,4 +99,4 @@ if __name__ == '__main__':
fixed_star = Procyon() fixed_star = Procyon()
print(fixed_star) print(fixed_star)
fixed_star.compare_with_sun() 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)
...@@ -76,4 +76,4 @@ if __name__ == '__main__': ...@@ -76,4 +76,4 @@ if __name__ == '__main__':
fixed_star = Rigel() fixed_star = Rigel()
print(fixed_star) print(fixed_star)
fixed_star.compare_with_sun() fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=78) fixed_star.density_by_radius(num_sun_radius=78)
...@@ -101,4 +101,4 @@ if __name__ == '__main__': ...@@ -101,4 +101,4 @@ if __name__ == '__main__':
fixed_star = Sirius() fixed_star = Sirius()
print(fixed_star) print(fixed_star)
fixed_star.compare_with_sun() 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)
...@@ -87,4 +87,4 @@ if __name__ == '__main__': ...@@ -87,4 +87,4 @@ if __name__ == '__main__':
fixed_star = Stephenson_2_18() fixed_star = Stephenson_2_18()
print(fixed_star) print(fixed_star)
fixed_star.compare_with_sun() fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=2150) fixed_star.density_by_radius(num_sun_radius=2150)
...@@ -75,4 +75,4 @@ if __name__ == '__main__': ...@@ -75,4 +75,4 @@ if __name__ == '__main__':
fixed_star = UYScuti() fixed_star = UYScuti()
print(fixed_star) print(fixed_star)
fixed_star.compare_with_sun() fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=1708) fixed_star.density_by_radius(num_sun_radius=1708)
...@@ -78,4 +78,4 @@ if __name__ == '__main__': ...@@ -78,4 +78,4 @@ if __name__ == '__main__':
fixed_star = VYCanisMajoris() fixed_star = VYCanisMajoris()
print(fixed_star) print(fixed_star)
fixed_star.compare_with_sun() fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=1400) fixed_star.density_by_radius(num_sun_radius=1400)
...@@ -76,4 +76,4 @@ if __name__ == '__main__': ...@@ -76,4 +76,4 @@ if __name__ == '__main__':
fixed_star = YCanumVenaticorum() fixed_star = YCanumVenaticorum()
print(fixed_star) print(fixed_star)
fixed_star.compare_with_sun() fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=215) fixed_star.density_by_radius(num_sun_radius=215)
...@@ -133,6 +133,82 @@ def get_acceleration_info(acceleration): ...@@ -133,6 +133,82 @@ def get_acceleration_info(acceleration):
acc_info = "0 m/s²" acc_info = "0 m/s²"
return acc_info 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): # def calculate_velocity(mass, semimajor_axis, eccentricity):
# """ # """
......
...@@ -33,6 +33,6 @@ if __name__ == '__main__': ...@@ -33,6 +33,6 @@ if __name__ == '__main__':
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹 # 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后- # position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_HOUR / 2, ursina_run(bodies, SECONDS_PER_HOUR / 2,
position=(1.5 * earth.raduis, 0, -30000), position=(1.5 * earth.radius, 0, -30000),
show_grid=False, show_grid=False,
view_closely=0.001) # 近距离观看 view_closely=True或0.001 view_closely=0.001) # 近距离观看 view_closely=True或0.001
...@@ -110,7 +110,7 @@ class EarthOrbitStoppedSim: ...@@ -110,7 +110,7 @@ class EarthOrbitStoppedSim:
# 计算地球和太阳中心点之间的距离 # 计算地球和太阳中心点之间的距离
distance = calculate_distance(self.earth.position, self.sun.position) 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: if distance > 100000000:
distance_str = "%s亿" % round(distance / 100000000.0, 2) distance_str = "%s亿" % round(distance / 100000000.0, 2)
......
...@@ -48,11 +48,11 @@ if __name__ == '__main__': ...@@ -48,11 +48,11 @@ if __name__ == '__main__':
if idx == 0: # 这是地球 if idx == 0: # 这是地球
d = 0 d = 0
else: 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 # 所有天体的初始速度为 0
body.init_velocity = [0, 0, 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 distance_sum += d
# 使用 ursina 查看的运行效果 # 使用 ursina 查看的运行效果
......
...@@ -48,11 +48,11 @@ if __name__ == '__main__': ...@@ -48,11 +48,11 @@ if __name__ == '__main__':
if idx == 0: # 这是地球 if idx == 0: # 这是地球
d = 0 d = 0
else: 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 # 所有天体的初始速度为 0
body.init_velocity = [0, 0, 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 distance_sum += d
# 使用 ursina 查看的运行效果 # 使用 ursina 查看的运行效果
......
...@@ -342,7 +342,7 @@ def two_bodies_colliding(body1: Body, body2: Body): ...@@ -342,7 +342,7 @@ def two_bodies_colliding(body1: Body, body2: Body):
d = calculate_distance(np.array(body1.position) * body1.distance_scale, d = calculate_distance(np.array(body1.position) * body1.distance_scale,
np.array(body2.position) * body2.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 True
return False return False
......
...@@ -46,8 +46,8 @@ def gen_bodies_from_image(pixel_image, params, texture="color_body.png"): ...@@ -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[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) 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 = [body.radius * SIZE_SCALE, (distance_sum + d), AU]
# body.init_position = [-(distance_sum + d), AU, body.raduis * SIZE_SCALE] # 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] # return pos[0] + (scale - 1.0) * 300 * (random.randint(90, 110)) * D, pos[1], pos[2]
......
...@@ -18,21 +18,21 @@ if __name__ == '__main__': ...@@ -18,21 +18,21 @@ if __name__ == '__main__':
# 创建一个地球,位于中心位置,初始速度为 0 # 创建一个地球,位于中心位置,初始速度为 0
earth = Earth(init_position=[0, 0, 0], init_velocity=[0, 0, 0], earth = Earth(init_position=[0, 0, 0], init_velocity=[0, 0, 0],
size_scale=1, texture="earth_hd.jpg") size_scale=1, texture="earth_hd.jpg")
# 地球的半径 = earth.raduis = 6373.22 # 地球的半径 = earth.radius = 6373.22
# 创建的3个不同质量,不同高度的球,观察这3个球打到地球表面上的加速度 # 创建的3个不同质量,不同高度的球,观察这3个球打到地球表面上的加速度
ball_1 = Football(mass=500, size_scale=2.65e2, trail_color=[255, 0, 0], ball_1 = Football(mass=500, size_scale=2.65e2, trail_color=[255, 0, 0],
# 球在地面上 518 多公里处 # 球在地面上 518 多公里处
# 518 = sqrt[(earth.raduis + 500)² + (-500)²] - earth.raduis # 518 = sqrt[(earth.radius + 500)² + (-500)²] - earth.radius
init_position=[-500, earth.raduis + 500, 0], init_position=[-500, earth.radius + 500, 0],
init_velocity=[0, 0, 0], gravity_only_for=[earth]) init_velocity=[0, 0, 0], gravity_only_for=[earth])
ball_2 = Football(mass=1000, size_scale=3.3e2, trail_color=[0, 255, 0], ball_2 = Football(mass=1000, size_scale=3.3e2, trail_color=[0, 255, 0],
# 球在地面上 800 多公里处 # 球在地面上 800 多公里处
init_position=[0, earth.raduis + 800, 0], init_position=[0, earth.radius + 800, 0],
init_velocity=[0, 0, 0], gravity_only_for=[earth]) init_velocity=[0, 0, 0], gravity_only_for=[earth])
ball_3 = Football(mass=5000, size_scale=3.8e2, trail_color=[0, 0, 255], ball_3 = Football(mass=5000, size_scale=3.8e2, trail_color=[0, 0, 255],
# 球在地面上 1016 多公里处 # 球在地面上 1016 多公里处
# 1016 = sqrt[(earth.raduis + 1000)² + 500²] - earth.raduis # 1016 = sqrt[(earth.radius + 1000)² + 500²] - earth.radius
init_position=[500, earth.raduis + 1000, 0], init_position=[500, earth.radius + 1000, 0],
init_velocity=[0, 0, 0], gravity_only_for=[earth]) init_velocity=[0, 0, 0], gravity_only_for=[earth])
bodies = [earth, ball_1, ball_2, ball_3] bodies = [earth, ball_1, ball_2, ball_3]
...@@ -41,7 +41,7 @@ if __name__ == '__main__': ...@@ -41,7 +41,7 @@ if __name__ == '__main__':
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹 # 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后- # position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_MINUTE, # 一秒相当于一分钟 ursina_run(bodies, SECONDS_PER_MINUTE, # 一秒相当于一分钟
position=(0, earth.raduis + 500, -4500), position=(0, earth.radius + 500, -4500),
show_trail=True, show_trail=True,
show_timer=True, show_timer=True,
view_closely=0.001) view_closely=0.001)
...@@ -9,33 +9,74 @@ ...@@ -9,33 +9,74 @@
from bodies import Sun, Jupiter, Earth from bodies import Sun, Jupiter, Earth
from objs import RockSnow, Rock, create_rock from objs import RockSnow, Rock, create_rock
from common.consts import SECONDS_PER_MONTH, SECONDS_PER_YEAR from common.consts import SECONDS_PER_MONTH, SECONDS_PER_YEAR
from sim_scenes.func import ursina_run, camera_look_at, two_bodies_colliding from sim_scenes.func import ursina_run, camera_look_at, two_bodies_colliding, create_text_panel
from simulators.ursina.entities.body_timer import TimeData from simulators.ursina.entities.body_timer import TimeData, BodyTimer
from simulators.ursina.entities.entity_utils import create_directional_light from simulators.ursina.entities.entity_utils import create_directional_light
from simulators.ursina.ursina_event import UrsinaEvent from simulators.ursina.ursina_event import UrsinaEvent
import random import random
import math
def create_comet(index, raduis, gravity_only_for): class JupiterProtectsEarthSim:
def __init__(self, comet_num=20):
"""
@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
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)
# 显示板信息模板
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]
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 index: 索引号
@param raduis: 木星的半径,保证生成的石头在木星半径外 @param radius: 木星的半径,保证生成的石头在木星半径外
@param gravity_only_for: 指定一个天体,石头只对该天体引力有效 @param gravity_only_for: 指定一个天体,石头只对该天体引力有效
@return: @return:
""" """
# 随机生成石头的位置和初始速度信息 # 随机生成石头的位置和初始速度信息
pos = [-raduis * random.randint(120, 200) / 100, pos, vel = self.random_pos_vel(radius)
-raduis * random.randint(120, 200) / 1000,
-raduis * random.randint(100, 300) / 100]
# 随机速度
vel = [-random.randint(90, 200) / 300, 0, 0]
# vel = [0, 0, 0] # vel = [0, 0, 0]
# 石头随机大小 # 石头随机大小
size_scale = random.randint(600, 1200) * 2e4 size_scale = random.randint(600, 1200) * 1.5e4
# 随机创建石头 # 随机创建石头
rock = create_rock( rock = create_rock(
no=index % 8 + 1, name=f'岩石{index + 1}', mass=4.4e10, no=index % 8 + 1, name=f'岩石{index + 1}', mass=size_scale / 1000,
size_scale=size_scale, color=(255, 200, 0), size_scale=size_scale, color=(255, 200, 0),
init_position=pos, init_velocity=vel, gravity_only_for=gravity_only_for init_position=pos, init_velocity=vel, gravity_only_for=gravity_only_for
) )
...@@ -45,81 +86,96 @@ def create_comet(index, raduis, gravity_only_for): ...@@ -45,81 +86,96 @@ def create_comet(index, raduis, gravity_only_for):
rock.rotation[random.randint(0, 2)] = random.randint(90, 200) / 100 rock.rotation[random.randint(0, 2)] = random.randint(90, 200) / 100
return rock return rock
def on_timer_changed(self, time_data: TimeData):
colliding_count = [0, 0, 0]
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]
for body in bodies:
body.rotation_speed /= 10 # 自转速度降低10倍
comets = []
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)
def on_timer_changed(time_data: TimeData):
# 运行中,每时每刻都会触发 # 运行中,每时每刻都会触发
for comet in comets: for comet in self.comets:
if comet.planet.enabled: if comet.planet.enabled:
collided = False
# 如果是否可见,则旋转石头 # 如果是否可见,则旋转石头
comet.planet.rotation += comet.rotation comet.planet.rotation += comet.rotation
# 循环判断每个石头与木星是否相碰撞,如果相碰撞就爆炸 # 循环判断每个石头与木星是否相碰撞,如果相碰撞就爆炸
if two_bodies_colliding(comet, jupiter): if two_bodies_colliding(comet, self.jupiter):
# 将石头隐藏、设置引力无效后,展示爆炸效果 # 将石头隐藏、设置引力无效后,展示爆炸效果
comet.explode(jupiter) # comet.explode(self.jupiter)
colliding_count[1] += 1 # 碰撞到木星,该石头不要爆炸,尝试看看是否会碰撞到地球,如果碰撞了地球,则“木星保护”统计加一
elif two_bodies_colliding(comet, sun): self.colliding_count[1] += 1
# 加入标记,说明该石头已经碰撞到木星
setattr(comet, "jupiter_collided", True)
# collided = True
elif two_bodies_colliding(comet, self.sun):
# 将石头隐藏、设置引力无效后,展示爆炸效果 # 将石头隐藏、设置引力无效后,展示爆炸效果
comet.explode(sun) comet.explode(self.sun)
colliding_count[0] += 1 if not hasattr(comet, "jupiter_collided"):
elif two_bodies_colliding(comet, earth): # 该石头没有碰撞到木星,才算一次
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) comet.explode(self.earth)
colliding_count[2] += 1 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(): if __name__ == '__main__':
""" """
当按键盘的 “O” 键重置后,恢复所有石头的状态(石头可见、引力有效),这样就可以反复观看 彗木保护地球模拟
@return:
""" """
# for comet in comets:
# comet.set_visible(True)
# comet.ignore_mass = False
# 设置计时器的最小时间单位为年
BodyTimer().min_unit = BodyTimer.MIN_UNIT_YEARS
def on_ready(): sim = JupiterProtectsEarthSim(comet_num=30)
# 运行前触发
# 为了较好的立体效果,可以增加太阳光线,光线指向木星(target=jupiter)
create_directional_light(position=(200, 0, -300), light_num=3, target=jupiter)
# 摄像机看向木星
camera_look_at(jupiter, rotation_z=0)
# 订阅事件后,上面3个函数功能才会起作用
# 按键盘的 “O” 重置键会触发 on_reset
# UrsinaEvent.on_reset_subscription(on_reset)
# 运行前会触发 on_ready # 运行前会触发 on_ready
# UrsinaEvent.on_ready_subscription(on_ready) UrsinaEvent.on_ready_subscription(sim.on_ready)
# 运行中,每时每刻都会触发 on_timer_changed # 运行中,每时每刻都会触发 on_timer_changed
UrsinaEvent.on_timer_changed_subscription(on_timer_changed) UrsinaEvent.on_timer_changed_subscription(sim.on_timer_changed)
# 使用 ursina 查看的运行效果 # 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹 # 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后- # position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_YEAR, ursina_run(sim.bodies, SECONDS_PER_MONTH * 3,
position=(30000000, 0, -3000000000), position=(30000000, 0, -3000000000),
show_timer=True) show_timer=True)
...@@ -14,11 +14,11 @@ from simulators.ursina.entities.body_timer import TimeData ...@@ -14,11 +14,11 @@ from simulators.ursina.entities.body_timer import TimeData
from simulators.ursina.ursina_event import UrsinaEvent 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 velocity: 抛出去的速度
@param raduis: 地球中心的半径 @param radius: 地球中心的半径
@param trail_color: 轨迹颜色 @param trail_color: 轨迹颜色
@param gravity_only_for: 指定一个天体,被抛的物体的引力只对该天体有效 @param gravity_only_for: 指定一个天体,被抛的物体的引力只对该天体有效
@param angle: 抛出去的角度(地平线夹角,默认为10) @param angle: 抛出去的角度(地平线夹角,默认为10)
...@@ -27,7 +27,7 @@ def create_ejected_object(velocity, raduis, trail_color, gravity_only_for, angle ...@@ -27,7 +27,7 @@ def create_ejected_object(velocity, raduis, trail_color, gravity_only_for, angle
# 根据速度、角度获取矢量速度(vx、vy) -> vx² + vy² = velocity² # 根据速度、角度获取矢量速度(vx、vy) -> vx² + vy² = velocity²
vx, vy = get_vector2d_velocity(velocity, angle=angle) vx, vy = get_vector2d_velocity(velocity, angle=angle)
football = Football(name=f'物体速度:{velocity}', mass=500, size_scale=1e3, 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]) init_velocity=[vx, vy, 0], gravity_only_for=[gravity_only_for])
return football return football
...@@ -39,19 +39,19 @@ if __name__ == '__main__': ...@@ -39,19 +39,19 @@ if __name__ == '__main__':
# 地球在中心位置 # 地球在中心位置
earth = Earth(init_position=[0, 0, 0], init_velocity=[0, 0, 0], earth = Earth(init_position=[0, 0, 0], init_velocity=[0, 0, 0],
size_scale=1, rotation_speed=0, texture="earth_hd.jpg") 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(第二宇宙速度) # 创建4个不同的抛出速度的物体,速度分别为:7.5km/s、8.5km/s、10km/s、11.2km/s(第二宇宙速度)
# velocity = 7.5,飞不出地球太远,就落地(仅适用于地球的重力,物体之间重力不要受到影响) # 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) # 被抛物只对该地球引力有效 gravity_only_for=earth) # 被抛物只对该地球引力有效
# velocity = 8.5,飞不出地球太远,就落地 # 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) # 被抛物只对该地球引力有效 gravity_only_for=earth) # 被抛物只对该地球引力有效
# velocity = 10,能飞出地球很远,但还是无法摆脱地球引力 # 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) # 被抛物只对该地球引力有效 gravity_only_for=earth) # 被抛物只对该地球引力有效
# velocity = 11.2,脱离地球引力直接飞出。速度11.2千米/秒为脱离地球引力的速度叫第二宇宙速度 # 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) # 被抛物只对该地球引力有效 gravity_only_for=earth) # 被抛物只对该地球引力有效
bodies = [earth, obj0, obj1, obj2, obj3] bodies = [earth, obj0, obj1, obj2, obj3]
......
...@@ -16,18 +16,18 @@ from simulators.ursina.ursina_event import UrsinaEvent ...@@ -16,18 +16,18 @@ from simulators.ursina.ursina_event import UrsinaEvent
import random import random
def create_comet(index, raduis, gravity_only_for): def create_comet(index, radius, gravity_only_for):
""" """
随机生成石头(随机位置、随机初始速度、随机大小、随机旋转) 随机生成石头(随机位置、随机初始速度、随机大小、随机旋转)
@param index: 索引号 @param index: 索引号
@param raduis: 木星的半径,保证生成的石头在木星半径外 @param radius: 木星的半径,保证生成的石头在木星半径外
@param gravity_only_for: 指定一个天体,石头只对该天体引力有效 @param gravity_only_for: 指定一个天体,石头只对该天体引力有效
@return: @return:
""" """
# 随机生成石头的位置和初始速度信息 # 随机生成石头的位置和初始速度信息
pos = [-raduis * random.randint(120, 200) / 100, pos = [-radius * random.randint(120, 200) / 100,
-raduis * random.randint(120, 200) / 1000, -radius * random.randint(120, 200) / 1000,
-raduis * random.randint(100, 300) / 100] -radius * random.randint(100, 300) / 100]
# 随机速度 # 随机速度
vel = [0, -random.randint(90, 200) / 30, 0] vel = [0, -random.randint(90, 200) / 30, 0]
# 石头随机大小 # 石头随机大小
...@@ -58,7 +58,7 @@ if __name__ == '__main__': ...@@ -58,7 +58,7 @@ if __name__ == '__main__':
for i in range(20): 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) bodies.append(comet)
comets.append(comet) comets.append(comet)
......
...@@ -103,8 +103,8 @@ class MplSimulator(Simulator): ...@@ -103,8 +103,8 @@ class MplSimulator(Simulator):
else: else:
color = 'blue' color = 'blue'
# size = 800 if str(body.name).lower().startswith("sun") else 500 # size = 800 if str(body.name).lower().startswith("sun") else 500
size = body.raduis * body.size_scale / 80000 size = body.radius * body.size_scale / 80000
# size = pow(body.raduis / AU * body.size_scale,3) # size = pow(body.radius / AU * body.size_scale,3)
pos = body.position / AU pos = body.position / AU
# 天体 # 天体
......
...@@ -55,8 +55,8 @@ class Simulator(metaclass=ABCMeta): ...@@ -55,8 +55,8 @@ class Simulator(metaclass=ABCMeta):
view.acceleration = body.acceleration view.acceleration = body.acceleration
view.velocity = body.velocity view.velocity = body.velocity
# viewer.volume = body.volume # viewer.volume = body.volume
if hasattr(body, "raduis"): if hasattr(body, "radius"):
view.raduis = body.raduis view.radius = body.radius
view.his_position = body.his_position() view.his_position = body.his_position()
if hasattr(body, "is_fixed_star"): if hasattr(body, "is_fixed_star"):
view.is_fixed_star = body.is_fixed_star view.is_fixed_star = body.is_fixed_star
......
...@@ -223,7 +223,7 @@ class Planet(Entity): ...@@ -223,7 +223,7 @@ class Planet(Entity):
if self.rotation_speed is None or dt == 0: if self.rotation_speed is None or dt == 0:
self.rotspeed = 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: else:
# 是通过月球保持一面面对地球,调整得到 # 是通过月球保持一面面对地球,调整得到
self.rotspeed = self.rotation_speed * (dt / 3600) / 2.4 * \ self.rotspeed = self.rotation_speed * (dt / 3600) / 2.4 * \
......
...@@ -23,7 +23,7 @@ from simulators.simulator import Simulator ...@@ -23,7 +23,7 @@ from simulators.simulator import Simulator
from common.system import System from common.system import System
from simulators.ursina.entities.world_grid import WorldGrid from simulators.ursina.entities.world_grid import WorldGrid
from simulators.ursina.entities.sphere_sky import SphereSky 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 datetime
import os import os
from ursina import EditorCamera from ursina import EditorCamera
...@@ -92,6 +92,19 @@ class UrsinaSimulator(Simulator): ...@@ -92,6 +92,19 @@ class UrsinaSimulator(Simulator):
def body_explode(target=None): def body_explode(target=None):
# from panda3d.core import GeomUtils # from panda3d.core import GeomUtils
if body.planet.enabled: 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.stop_and_ignore_gravity()
body.planet.enabled = False body.planet.enabled = False
...@@ -103,7 +116,7 @@ class UrsinaSimulator(Simulator): ...@@ -103,7 +116,7 @@ class UrsinaSimulator(Simulator):
scale = 3 * volume_scale * body.size_scale * UrsinaConfig.SCALE_FACTOR scale = 3 * volume_scale * body.size_scale * UrsinaConfig.SCALE_FACTOR
print(scale, body) print(scale, body)
explode_ani = Animation(explosion_file, explode_ani = Animation(explosion_file,
position=body.planet.position, position=explode_pos,
scale=scale, fps=6, scale=scale, fps=6,
loop=False, autoplay=True) loop=False, autoplay=True)
explode_ani.set_light_off() explode_ani.set_light_off()
......
...@@ -39,8 +39,8 @@ class BodyView(metaclass=ABCMeta): ...@@ -39,8 +39,8 @@ class BodyView(metaclass=ABCMeta):
self.name = body.name self.name = body.name
self.mass = body.mass self.mass = body.mass
if hasattr(body, "raduis"): if hasattr(body, "radius"):
self.raduis = body.raduis self.radius = body.radius
self.velocity = body.velocity self.velocity = body.velocity
...@@ -48,7 +48,7 @@ class BodyView(metaclass=ABCMeta): ...@@ -48,7 +48,7 @@ class BodyView(metaclass=ABCMeta):
def __repr__(self): def __repr__(self):
return '<%s> m=%.3e(kg), r=%.3e(km), p=[%.3e,%.3e,%.3e](km), v=%s(km/s)' % \ 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) self.position[0], self.position[1], self.position[2], self.velocity)
def __find_texture(self, texture): def __find_texture(self, texture):
......
...@@ -70,7 +70,7 @@ class MayaviView(BodyView): ...@@ -70,7 +70,7 @@ class MayaviView(BodyView):
self.body.size_scale * rings_scale + \ self.body.size_scale * rings_scale + \
self.body.position[1] # * self.body.distance_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]) + \ torus[2][i][j] = thicknesses_scale * np.sin(phi[j]) + \
self.body.position[2] # * self.body.distance_scale self.body.position[2] # * self.body.distance_scale
rings_color = (173 / 255, 121 / 255, 92 / 255) rings_color = (173 / 255, 121 / 255, 92 / 255)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册