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

Python超人-宇宙模拟器

上级 f5227d2e
...@@ -47,9 +47,9 @@ class Asteroid(Body): ...@@ -47,9 +47,9 @@ class Asteroid(Body):
def show_trail(self): def show_trail(self):
return False return False
def ignore_gravity(self, body): def ignore_gravity_with(self, body):
""" """
是否忽略引力 是否忽略指定天体的引力
@param body: @param body:
@return: @return:
""" """
......
...@@ -50,9 +50,9 @@ class Asteroids(Body): ...@@ -50,9 +50,9 @@ class Asteroids(Body):
# 环状星群 # 环状星群
self.torus_stars = True self.torus_stars = True
def ignore_gravity(self, body): def ignore_gravity_with(self, body):
""" """
是否忽略引力 是否忽略指定天体的引力
@param body: @param body:
@return: @return:
""" """
......
...@@ -38,6 +38,7 @@ class Body(metaclass=ABCMeta): ...@@ -38,6 +38,7 @@ class Body(metaclass=ABCMeta):
@param rotation_speed: 自旋速度(度/小时) @param rotation_speed: 自旋速度(度/小时)
@param parent: 天体的父对象 @param parent: 天体的父对象
@param ignore_mass: 是否忽略质量(如果为True,则不计算引力) @param ignore_mass: 是否忽略质量(如果为True,则不计算引力)
TODO: 注意:这里的算法是基于牛顿的万有引力(质量为0不受引力的影响在天体物理学中是不严谨)
@param is_fixed_star: 是否为恒星 @param is_fixed_star: 是否为恒星
@param trail_color: 天体拖尾颜色(默认天体颜色) @param trail_color: 天体拖尾颜色(默认天体颜色)
@param show_name: 是否显示天体名称 @param show_name: 是否显示天体名称
...@@ -53,10 +54,11 @@ class Body(metaclass=ABCMeta): ...@@ -53,10 +54,11 @@ class Body(metaclass=ABCMeta):
self.name = name self.name = name
self.__mass = mass self.__mass = mass
# 是否忽略质量(如果为True,则不计算引力)
# TODO: 注意:这里的算法是基于牛顿的万有引力(质量为0不受引力的影响在天体物理学中是不严谨)
if self.__mass <= 0: # 质量小于等于0就忽略 if self.__mass <= 0: # 质量小于等于0就忽略
self.ignore_mass = True self.ignore_mass = True
else: else:
# 是否忽略质量(如果为True,则不计算引力)
self.ignore_mass = ignore_mass self.ignore_mass = ignore_mass
self.__init_position = None self.__init_position = None
...@@ -97,12 +99,13 @@ class Body(metaclass=ABCMeta): ...@@ -97,12 +99,13 @@ class Body(metaclass=ABCMeta):
self.__has_rings = False self.__has_rings = False
def set_ignore_mass(self, value=True): def set_ignore_gravity(self, value=True):
""" """
设置忽略质量,True为引力失效 设置忽略质量,True为引力失效
@param value: @param value:
@return: @return:
""" """
# TODO: 注意:这里的算法是基于牛顿的万有引力(质量为0不受引力的影响在天体物理学中是不严谨)
if self.__mass <= 0: # 质量小于等于0就忽略 if self.__mass <= 0: # 质量小于等于0就忽略
self.ignore_mass = True self.ignore_mass = True
else: else:
...@@ -220,9 +223,26 @@ class Body(metaclass=ABCMeta): ...@@ -220,9 +223,26 @@ class Body(metaclass=ABCMeta):
@param value: @param value:
@return: @return:
""" """
self.__acceleration = value self.__acceleration = np.array(value, dtype=float)
self.__record_history() self.__record_history()
def stop(self):
"""
停止运动,将加速度和速度置零
@return:
"""
self.init_velocity = [0.0, 0.0, 0.0]
self.acceleration = [0.0, 0.0, 0.0]
def stop_and_ignore_gravity(self):
"""
停止运动,并忽略质量(不受引力影响)
TODO: 注意:这里的算法是基于牛顿的万有引力(质量为0不受引力的影响在天体物理学中是不严谨)
@return:
"""
self.set_ignore_gravity()
self.stop()
@property @property
def velocity(self): def velocity(self):
""" """
...@@ -359,12 +379,15 @@ class Body(metaclass=ABCMeta): ...@@ -359,12 +379,15 @@ class Body(metaclass=ABCMeta):
(self.name, self.__class__.__name__, self.mass, self.raduis, self.diameter, self.volume, self.density, (self.name, self.__class__.__name__, self.mass, self.raduis, 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(self, body): def ignore_gravity_with(self, body):
""" """
是否忽略引力 是否忽略指定天体的引力
@param body: @param body:
@return: @return:
""" """
# TODO: 注意:这里的算法是基于牛顿的万有引力(质量为0不受引力的影响在天体物理学中是不严谨)
if self.ignore_mass:
return True
return False return False
......
...@@ -39,13 +39,14 @@ class DysenSphere(Body): ...@@ -39,13 +39,14 @@ class DysenSphere(Body):
"parent": parent "parent": parent
} }
super().__init__(**params) super().__init__(**params)
# TODO: 注意:这里的算法是基于牛顿的万有引力(质量为0不受引力的影响在天体物理学中是不严谨)
self.ignore_mass = True self.ignore_mass = True
# 灯光禁用 # 灯光禁用
self.light_disable = True self.light_disable = True
def ignore_gravity(self, body): def ignore_gravity_with(self, body):
""" """
是否忽略引力 是否忽略指定天体的引力
@param body: @param body:
@return: @return:
""" """
......
...@@ -39,6 +39,7 @@ class Moon(Body): ...@@ -39,6 +39,7 @@ class Moon(Body):
@param distance_scale: 距离缩放 @param distance_scale: 距离缩放
@param rotation_speed: 自旋速度(度/小时) @param rotation_speed: 自旋速度(度/小时)
@param ignore_mass: 是否忽略质量(如果为True,则不计算引力) @param ignore_mass: 是否忽略质量(如果为True,则不计算引力)
TODO: 注意:这里的算法是基于牛顿的万有引力(质量为0不受引力的影响在天体物理学中是不严谨)
@param trail_color:月球拖尾颜色(默认天体颜色) @param trail_color:月球拖尾颜色(默认天体颜色)
@param show_name: 是否显示月球名称 @param show_name: 是否显示月球名称
@param gravity_only_for_earth: 如果为True,则仅适用于地球的重力,与其他天体之间的重力不会受到影响 @param gravity_only_for_earth: 如果为True,则仅适用于地球的重力,与其他天体之间的重力不会受到影响
...@@ -61,12 +62,15 @@ class Moon(Body): ...@@ -61,12 +62,15 @@ class Moon(Body):
super().__init__(**params) super().__init__(**params)
self.gravity_only_for_earth = gravity_only_for_earth self.gravity_only_for_earth = gravity_only_for_earth
def ignore_gravity(self, body): def ignore_gravity_with(self, body):
""" """
是否忽略引力 是否忽略指定天体的引力
@param body: @param body:
@return: @return:
""" """
if self.ignore_mass:
return True
if self.gravity_only_for_earth: if self.gravity_only_for_earth:
# 月球只对地球有引力,忽略其他的引力 # 月球只对地球有引力,忽略其他的引力
if isinstance(body, Earth): if isinstance(body, Earth):
......
...@@ -186,7 +186,7 @@ class System(object): ...@@ -186,7 +186,7 @@ class System(object):
if body1 is body2: if body1 is body2:
continue continue
elif body1.ignore_gravity(body2) or body2.ignore_gravity(body1): elif body1.ignore_gravity_with(body2) or body2.ignore_gravity_with(body1):
continue continue
r = body2.position - body1.position r = body2.position - body1.position
......
...@@ -14,11 +14,13 @@ from bodies.body import Body, AU ...@@ -14,11 +14,13 @@ from bodies.body import Body, AU
if __name__ == '__main__': if __name__ == '__main__':
""" """
恒星演示 恒星演示
""" """
# 构建恒星天体对象 # 构建恒星天体对象
D = 5e5 # 基本距离单位:km(随意赋值) D = 5e5 # 基本距离单位:km(随意赋值)
SIZE_SCALE = 0.5 # 所有天体尺寸缩放保持一致 SIZE_SCALE = 0.5 # 所有天体尺寸缩放保持一致
# TODO: ignore_mass=True
# 注意:这里的算法是基于牛顿的万有引力(质量为0不受引力的影响在天体物理学中是不严谨)
bodies = [ bodies = [
Earth(size_scale=SIZE_SCALE, ignore_mass=True), Earth(size_scale=SIZE_SCALE, ignore_mass=True),
Sun(size_scale=SIZE_SCALE, ignore_mass=True), # 太阳 Sun(size_scale=SIZE_SCALE, ignore_mass=True), # 太阳
......
...@@ -19,6 +19,8 @@ if __name__ == '__main__': ...@@ -19,6 +19,8 @@ if __name__ == '__main__':
# 构建恒星天体对象 # 构建恒星天体对象
D = 5e5 # 基本距离单位:km(随意赋值) D = 5e5 # 基本距离单位:km(随意赋值)
SIZE_SCALE = 0.5 # 所有天体尺寸缩放保持一致 SIZE_SCALE = 0.5 # 所有天体尺寸缩放保持一致
# TODO: ignore_mass=True
# 注意:这里的算法是基于牛顿的万有引力(质量为0不受引力的影响在天体物理学中是不严谨)
bodies = [ bodies = [
Earth(size_scale=SIZE_SCALE, ignore_mass=True), Earth(size_scale=SIZE_SCALE, ignore_mass=True),
Sun(size_scale=SIZE_SCALE, ignore_mass=True), # 太阳 Sun(size_scale=SIZE_SCALE, ignore_mass=True), # 太阳
......
...@@ -8,11 +8,14 @@ ...@@ -8,11 +8,14 @@
# ============================================================================== # ==============================================================================
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY, SECONDS_PER_HALF_DAY from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY, SECONDS_PER_HALF_DAY
from common.func import calculate_distance
from common.system import System from common.system import System
from bodies import Body
from simulators.ursina.ursina_config import UrsinaConfig from simulators.ursina.ursina_config import UrsinaConfig
from simulators.ursina.ursina_event import UrsinaEvent from simulators.ursina.ursina_event import UrsinaEvent
from common.consts import LIGHT_SPEED from common.consts import LIGHT_SPEED
import math import math
import numpy as np
def mayavi_run(bodies, dt=SECONDS_PER_WEEK, def mayavi_run(bodies, dt=SECONDS_PER_WEEK,
...@@ -202,7 +205,7 @@ def create_solar_system_bodies(ignore_mass=False, init_velocity=None): ...@@ -202,7 +205,7 @@ def create_solar_system_bodies(ignore_mass=False, init_velocity=None):
# 遍历所有天体, # 遍历所有天体,
for idx, body in enumerate(bodies): for idx, body in enumerate(bodies):
body.set_ignore_mass(ignore_mass) # 忽略质量(引力无效) body.set_ignore_gravity(ignore_mass) # 忽略质量(引力无效)
if init_velocity is not None: if init_velocity is not None:
body.init_velocity = init_velocity body.init_velocity = init_velocity
return bodies return bodies
...@@ -262,6 +265,29 @@ def get_vector2d_velocity(velocity, angle=15): ...@@ -262,6 +265,29 @@ def get_vector2d_velocity(velocity, angle=15):
return vx, vy return vx, vy
def two_bodies_colliding(body1: Body, body2: Body):
"""
判断两个天体是否相撞
@param body1:
@param body2:
@return:
"""
if hasattr(body1, "planet") and hasattr(body2, "planet"):
# 使用 Ursina 的算法
if hasattr(body1.planet, "intersects"):
return body1.planet.intersects(body2.planet).hit
# 自行实现的算法,两物体的距离小于两物体半径的和,就视为碰撞了
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:
return True
return False
# raise Exception("two_bodies_colliding 不支持类型[body1 body2]")
if __name__ == '__main__': if __name__ == '__main__':
from bodies import Sun, Earth from bodies import Sun, Earth
......
...@@ -27,7 +27,10 @@ def show_text_bodies(): ...@@ -27,7 +27,10 @@ def show_text_bodies():
pixel_image="./images/china.png", pixel_image="./images/china.png",
texture="color_body.png", texture="color_body.png",
params={"camera_pos": camera_pos}) params={"camera_pos": camera_pos})
# 放一个恒星作为背景 # 放一个恒星作为背景
# TODO: ignore_mass=True
# 注意:这里的算法是基于牛顿的万有引力(质量为0不受引力的影响在天体物理学中是不严谨)
bg = FixedStar(name="背景恒星", texture="fixed_star.png", bg = FixedStar(name="背景恒星", texture="fixed_star.png",
mass=2e32, color=(0xff, 0xf8, 0xd4), mass=2e32, color=(0xff, 0xf8, 0xd4),
init_position=[-450 * D, 100 * D, 6000 * D], # [ 左-右+, 上+下-, 远+近- ] init_position=[-450 * D, 100 * D, 6000 * D], # [ 左-右+, 上+下-, 远+近- ]
......
...@@ -24,7 +24,10 @@ def show_eye_of_god(): ...@@ -24,7 +24,10 @@ def show_eye_of_god():
bodies = gen_bodies_from_image(pixel_image="./images/eye.png", bodies = gen_bodies_from_image(pixel_image="./images/eye.png",
texture="color_body.jpg", texture="color_body.jpg",
params={"camera_pos": camera_pos}) params={"camera_pos": camera_pos})
# 放一个恒星作为背景 # 放一个恒星作为背景
# TODO: ignore_mass=True
# 注意:这里的算法是基于牛顿的万有引力(质量为0不受引力的影响在天体物理学中是不严谨)
bg = FixedStar(name="背景恒星", texture="fixed_star.png", bg = FixedStar(name="背景恒星", texture="fixed_star.png",
mass=2e32, color=(0xff, 0xf8, 0xd4), mass=2e32, color=(0xff, 0xf8, 0xd4),
init_position=[-400 * D, 100 * D, 6000 * D], # [ 左-右+, 上+下-, 远+近- ] init_position=[-400 * D, 100 * D, 6000 * D], # [ 左-右+, 上+下-, 远+近- ]
......
...@@ -8,7 +8,9 @@ ...@@ -8,7 +8,9 @@
# ============================================================================== # ==============================================================================
from bodies import Moon, Earth, Body from bodies import Moon, Earth, Body
from common.consts import SECONDS_PER_HOUR, SECONDS_PER_MINUTE from common.consts import SECONDS_PER_HOUR, SECONDS_PER_MINUTE
from sim_scenes.func import ursina_run, get_vector2d_velocity from sim_scenes.func import ursina_run, get_vector2d_velocity, camera_look_at, two_bodies_colliding
from simulators.ursina.entities.body_timer import TimeData
from simulators.ursina.ursina_event import UrsinaEvent
def create_ejected_object(velocity, raduis, trail_color, angle=10): def create_ejected_object(velocity, raduis, trail_color, angle=10):
...@@ -33,16 +35,38 @@ if __name__ == '__main__': ...@@ -33,16 +35,38 @@ if __name__ == '__main__':
抛物线模拟 抛物线模拟
""" """
# 地球在中心位置 # 地球在中心位置
e = Earth(init_position=[0, 0, 0], size_scale=1, texture="earth_hd.jpg", init_velocity=[0, 0, 0]) earth = Earth(init_position=[0, 0, 0], size_scale=1, texture="earth_hd.jpg", init_velocity=[0, 0, 0])
raduis = e.raduis + 300 raduis = earth.raduis + 300
# 红色:velocity = 8 # 物体飞不出地球太远,就落地 # TODO: 4个不同的抛出速度 7.5km/s、8.5km/s、10km/s、11.2km/s(第二宇宙速度)
obj1 = create_ejected_object(velocity=8, raduis=raduis, trail_color=(255, 0, 0)) # 粉色:velocity = 7.5,飞不出地球太远,就落地
# 绿色:velocity = 10 # 物体能飞出地球很远,但还是无法摆脱地球引力 obj0 = create_ejected_object(velocity=7.5, raduis=raduis, trail_color=(255, 0, 255))
# 红色:velocity = 8.5,飞不出地球太远,就落地
obj1 = create_ejected_object(velocity=8.5, raduis=raduis, trail_color=(255, 0, 0))
# 绿色:velocity = 10,能飞出地球很远,但还是无法摆脱地球引力
obj2 = create_ejected_object(velocity=10, raduis=raduis, trail_color=(0, 255, 0)) obj2 = create_ejected_object(velocity=10, raduis=raduis, trail_color=(0, 255, 0))
# 蓝色: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, raduis=raduis, trail_color=(0, 0, 255))
bodies = [e, obj1, obj2, obj3] bodies = [earth, obj0, obj1, obj2, obj3]
def on_timer_changed(time_data: TimeData):
"""
定时触发,在10分钟后,进行碰撞判断,如果抛出物与地球相碰撞了,则抛出物体静止不动
@param time_data:
@return:
"""
if time_data.total_minutes > 10:
# 抛出物体10分钟后,再进行碰撞判断
for obj in [obj0, obj1, obj2, obj3]:
# 循环判断每个抛出物与地球是否相碰撞
if two_bodies_colliding(obj, earth):
# 如果抛出物与地球相碰撞了,则静止不动(抛出物停止并忽略引力)
obj.stop_and_ignore_gravity()
# 订阅计时器事件(定时触发)
UrsinaEvent.on_timer_changed_subscription(on_timer_changed)
# 使用 ursina 查看的运行效果 # 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹 # 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
......
...@@ -17,7 +17,7 @@ if __name__ == '__main__': ...@@ -17,7 +17,7 @@ if __name__ == '__main__':
""" """
earth = Earth(texture="earth_hd.jpg", earth = Earth(texture="earth_hd.jpg",
init_position=[0, 0, 0], init_velocity=[0, 0, 0], init_position=[0, 0, 0], init_velocity=[0, 0, 0],
size_scale=1).set_ignore_mass() size_scale=1).set_ignore_gravity()
# 创建带有云层的地球 # 创建带有云层的地球
earth_with_clouds = Earth(texture="earth_hd.jpg", earth_with_clouds = Earth(texture="earth_hd.jpg",
......
...@@ -19,6 +19,8 @@ if __name__ == '__main__': ...@@ -19,6 +19,8 @@ if __name__ == '__main__':
""" """
resolution = 50 resolution = 50
# resolution = 500 # resolution = 500
# TODO: ignore_mass=True
# 注意:这里的算法是基于牛顿的万有引力(质量为0不受引力的影响在天体物理学中是不严谨)
bodies = [ bodies = [
Earth(texture="earth_at_night_hd.jpg", Earth(texture="earth_at_night_hd.jpg",
init_position=[0, 0, 0], init_velocity=[0, 0, 0], init_position=[0, 0, 0], init_velocity=[0, 0, 0],
......
...@@ -18,6 +18,7 @@ camera_follow_light = 'SideView' # 摄像机跟随光,方向是侧面看 ...@@ -18,6 +18,7 @@ camera_follow_light = 'SideView' # 摄像机跟随光,方向是侧面看
# 实例化一个初始化对象(订阅事件,记录到达每个行星所需要的时间) # 实例化一个初始化对象(订阅事件,记录到达每个行星所需要的时间)
init = SpeedOfLightInit(camera_follow_light) init = SpeedOfLightInit(camera_follow_light)
# TODO: 注意:这里的算法是基于牛顿的万有引力(质量为0不受引力的影响在天体物理学中是不严谨)
# 创建太阳系天体(忽略质量,引力无效,初速度全部为0) # 创建太阳系天体(忽略质量,引力无效,初速度全部为0)
bodies = create_solar_system_bodies(ignore_mass=True, init_velocity=[0, 0, 0]) bodies = create_solar_system_bodies(ignore_mass=True, init_velocity=[0, 0, 0])
......
...@@ -41,6 +41,15 @@ class TimeData: ...@@ -41,6 +41,15 @@ class TimeData:
else: else:
self.time_text = f'{self.hours:02d}:{self.minutes:02d}:{self.seconds:02d}' self.time_text = f'{self.hours:02d}:{self.minutes:02d}:{self.seconds:02d}'
@property
def total_minutes(self):
return self.total_seconds / 60
@property
def total_hours(self):
return self.total_seconds / 3600
class BodyTimer(Singleton): class BodyTimer(Singleton):
""" """
......
...@@ -73,7 +73,7 @@ class Planet(Entity): ...@@ -73,7 +73,7 @@ class Planet(Entity):
scale=scale, scale=scale,
texture=texture, texture=texture,
color=self.plant_color, color=self.plant_color,
# collider="sphere", collider="sphere",
position=pos, position=pos,
rotation=rotation, rotation=rotation,
double_sided=True double_sided=True
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册