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

太阳系三体模拟器

上级 8ec4d278
......@@ -12,3 +12,4 @@ from bodies.venus import Venus
from bodies.moon import Moon
from bodies.asteroid import Asteroid
from bodies.asteroids import Asteroids
from bodies.dysen_sphere import DysenSphere
# -*- coding:utf-8 -*-
# title :太阳
# description :太阳
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies.body import Body
class DysenSphere(Body):
"""
戴森球
------------------------
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="DysenSphere", mass=2e28,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(170, 98, 25),
texture="dysen_sphere.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.1,
parent=None):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 1.6,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"parent": parent
}
super().__init__(**params)
self.ignore_mass = True
# 灯光禁用
self.light_disable = True
def ignore_gravity(self, body):
"""
是否忽略引力
:param body:
:return:
"""
return True
if __name__ == '__main__':
print(DysenSphere())
......@@ -7,6 +7,7 @@
# python_version :3.8
# ==============================================================================
import numpy as np
import math
from common.consts import AU, G
from bodies import Body, Sun, Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto
from common.func import calculate_distance
......@@ -24,8 +25,53 @@ class System(object):
:param max_distance:系统的最大范围,超出范围的天体就不显示了
"""
self.bodies = bodies
# self.adjust_distance_and_velocity()
self.max_distance = max_distance
@staticmethod
def calc_body_new_velocity_position(body, sun_mass=1.9891e30, G=6.674e-11):
old_velocity = body.init_velocity
old_position = body.init_position
old_distance = np.linalg.norm(old_position - [0,0,0], axis=-1)
new_distance = old_distance * body.distance_scale
new_position = old_position * body.distance_scale
new_velocity = System.get_new_velocity(old_velocity, old_distance, new_distance, body.mass)
return new_velocity, new_position
@staticmethod
def get_new_velocity(old_velocity, old_distance, new_distance, mass, sun_mass=1.9891e30, G=6.674e-11):
# 计算原速度的模长
old_speed = np.linalg.norm(old_velocity * 1000)
# 计算原动能和原势能
old_kinetic_energy = 0.5 * mass * old_speed ** 2
old_potential_energy = - G * mass * sun_mass / old_distance
new_potential_energy = - G * mass * sun_mass / new_distance
# 计算新动能
new_kinetic_energy = old_kinetic_energy
# 计算新速度的模长
new_speed = math.sqrt(2 * (new_kinetic_energy - old_potential_energy) / mass)
# 计算新速度向量
new_velocity = old_velocity / old_speed * new_speed/1000
return new_velocity
def get_new_velocity1(old_velocity, old_distance, new_distance, mass, sun_mass=1.9891e30, G=6.674e-11):
# 计算原来的速度
old_speed = math.sqrt(G * sun_mass / old_distance)
# 计算新的速度
new_speed = math.sqrt(G * sun_mass / new_distance)
# 计算原来的动能
old_kinetic_energy = 0.5 * mass * old_velocity ** 2
# 计算新的动能
new_kinetic_energy = old_kinetic_energy * new_speed ** 2 / old_speed ** 2
# 计算新的速度
new_velocity = math.sqrt(2 * new_kinetic_energy / mass)
return new_velocity
def add(self, body):
self.bodies.append(body)
......@@ -166,30 +212,39 @@ if __name__ == '__main__':
# Neptune(), # 海王星
# Pluto() # 冥王星(从太阳系的行星中排除)
# ])
import math
mass = 2e30
r = 2 * AU
# p = 14.9
p = 14.89
bodies = [
Sun(name="太阳A红色", mass=mass,
init_position=[0, r * math.sqrt(3), 0], # 位置
init_velocity=[-p, 0, 0], # 速度(km/s)
size_scale=5e1, texture="sun2.jpg", color=(255, 0, 0)), # 太阳放大 100 倍
Sun(name="太阳B绿色", mass=mass,
init_position=[-r, 0, 0],
init_velocity=[1 / 2 * p, -math.sqrt(3) / 2 * p, 0],
size_scale=5e1, texture="sun2.jpg", color=(0, 255, 0)), # 太阳放大 100 倍
Sun(name="太阳C蓝色", mass=mass,
init_position=[r, 0, 0],
init_velocity=[1 / 2 * p, math.sqrt(3) / 2 * p, 0],
size_scale=5e1, texture="sun2.jpg", color=(0, 0, 255)), # 太阳放大 100 倍
Earth(name="地球",
# init_position=[0, -AU * -2, 5 * AU],
init_position=[0, math.sqrt(3) * r / 6, 5 * AU],
init_velocity=[0, 0, -10],
size_scale=4e3, distance_scale=1), # 地球放大 4000 倍,距离保持不变
]
body_sys = System(bodies)
print(body_sys.save_to_json("../data/tri_bodies_sim_perfect_01.json"))
# import math
#
# mass = 2e30
# r = 2 * AU
# # p = 14.9
# p = 14.89
# bodies = [
# Sun(name="太阳A红色", mass=mass,
# init_position=[0, r * math.sqrt(3), 0], # 位置
# init_velocity=[-p, 0, 0], # 速度(km/s)
# size_scale=5e1, texture="sun2.jpg", color=(255, 0, 0)), # 太阳放大 100 倍
# Sun(name="太阳B绿色", mass=mass,
# init_position=[-r, 0, 0],
# init_velocity=[1 / 2 * p, -math.sqrt(3) / 2 * p, 0],
# size_scale=5e1, texture="sun2.jpg", color=(0, 255, 0)), # 太阳放大 100 倍
# Sun(name="太阳C蓝色", mass=mass,
# init_position=[r, 0, 0],
# init_velocity=[1 / 2 * p, math.sqrt(3) / 2 * p, 0],
# size_scale=5e1, texture="sun2.jpg", color=(0, 0, 255)), # 太阳放大 100 倍
# Earth(name="地球",
# # init_position=[0, -AU * -2, 5 * AU],
# init_position=[0, math.sqrt(3) * r / 6, 5 * AU],
# init_velocity=[0, 0, -10],
# size_scale=4e3, distance_scale=1), # 地球放大 4000 倍,距离保持不变
# ]
# body_sys = System(bodies)
# print(body_sys.save_to_json("../data/tri_bodies_sim_perfect_01.json"))
earth = Earth(name="地球",
# init_position=[0, -AU * -2, 5 * AU],
init_position=[0, 1000000, 500000],
init_velocity=[0, 0, -10],
size_scale=4e3, distance_scale=1)
new_velocity, new_position = System.calc_body_new_velocity_position(earth)
print(new_velocity, new_position)
print(earth.init_velocity, earth.init_position)
\ No newline at end of file
# -*- coding:utf-8 -*-
# title :太阳、地球场景模拟
# description :太阳、地球场景模拟
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Sun, DysenSphere
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY, AU
from scenes.func import mayavi_run, ursina_run
if __name__ == '__main__':
"""
太阳、戴森球
"""
sun = Sun(size_scale=5e1, init_velocity=[0, 2, 0]) # 太阳放大 50 倍
bodies = [
sun,
DysenSphere(size_scale=5e1, parent=sun), # 戴森球放大 50 倍
]
# 使用 mayavi 查看的运行效果
# mayavi_run(bodies, SECONDS_PER_WEEK, view_azimuth=-45)
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_WEEK, position=(0, AU / 2, -3 * AU))
......@@ -21,8 +21,15 @@ from simulators.ursina.ui.event_handler import EventHandler
class ControlHandler(EventHandler):
"""
控制面板事件处理类
"""
def handler_input_init(self):
"""
输入事件初始化
@return:
"""
self.settings_handler = Entity(ignore_paused=True)
self.settings_handler.input = self.settings_handler_input
key_info_str = "方位控制[键盘QWEASD]+[鼠标右键],按[空格]更多控制"
......@@ -30,7 +37,10 @@ class ControlHandler(EventHandler):
background=True)
def sec_per_time_switch_changed(self):
# ("默认", "天", "周", "月", "年", "十年", "百年")
"""
按钮组("默认", "天", "周", "月", "年", "十年", "百年") 点击
@return:
"""
if self.ui.sec_per_time_switch.value == "天":
UrsinaConfig.seconds_per = SECONDS_PER_DAY
elif self.ui.sec_per_time_switch.value == "周":
......@@ -44,15 +54,19 @@ class ControlHandler(EventHandler):
elif self.ui.sec_per_time_switch.value == "百年":
UrsinaConfig.seconds_per = SECONDS_PER_YEAR * 100
else:
UrsinaConfig.seconds_per = 0
UrsinaConfig.seconds_per = 0 # 默认
def on_off_trail_changed(self):
"""
拖尾开关点击
@return:
"""
if self.ui.on_off_trail.value == self.ui.trail_button_text:
UrsinaConfig.show_trail = True
else:
UrsinaConfig.show_trail = False
def move_camera_to_entity(self, camera_pos: Vec3, entity_pos: Vec3, _distance: float) -> Vec3:
def move_camera_to_entity2(self, camera_pos: Vec3, entity_pos: Vec3, _distance: float) -> Vec3:
# 计算摄像机到实体的向量
direction = entity_pos - camera_pos
# 计算当前距离
......@@ -70,8 +84,14 @@ class ControlHandler(EventHandler):
def move_camera_to_entity(self, entity, d):
camera.position = entity.position # - Vec3(0, 0, d) # 设置摄像机位置
camera.world_position = entity.position
forward = camera.forward
def bodies_button_list_click(self, item):
def search_bodies_button_list_click(self, item):
"""
@param item:
@return:
"""
if item is not None:
# TODO: 先找到位置,确定摄像机的位置
try:
......@@ -80,12 +100,12 @@ class ControlHandler(EventHandler):
except Exception as e:
self.ui.show_message(f"{item}飞不见了")
self.bodies_button_list_close()
self.search_bodies_button_list_close()
def bodies_button_list_close(self):
if hasattr(self, "bodies_button_list"):
self.bodies_button_list.enabled = False
destroy(self.bodies_button_list)
def search_bodies_button_list_close(self):
if hasattr(self, "search_bodies_button_list"):
self.search_bodies_button_list.enabled = False
destroy(self.search_bodies_button_list)
def on_searching_bodies_click(self):
results = UrsinaEvent.on_searching_bodies()
......@@ -93,14 +113,13 @@ class ControlHandler(EventHandler):
sub_name, bodies = results[0]
if len(bodies) == 0:
self.ui.show_message("天体都飞不见了,请重新运行。")
# button_dict = {"天体都飞不见了,请重新运行。": lambda: self.bodies_button_list_click(None)}
return
# print(results[0])
button_dict = {"[关闭] == 寻找天体 ==": lambda: self.bodies_button_list_click(None)}
button_dict = {"[关闭] == 寻找天体 ==": lambda: self.search_bodies_button_list_click(None)}
camera = scene.camera
for body in bodies:
def callback_action(b=body):
self.bodies_button_list_click(b)
self.search_bodies_button_list_click(b)
if body.appeared:
distance_to_entity = distance(body.planet, camera)
......@@ -108,18 +127,18 @@ class ControlHandler(EventHandler):
name = f"{body.name}\t距离:{d:.4f}天文单位"
button_dict[name] = callback_action
else:
if hasattr(self, "bodies_button_list"):
self.bodies_button_list_close()
if hasattr(self, "search_bodies_button_list"):
self.search_bodies_button_list_close()
name = f"{body.name}\t距离太远,找不到了"
button_dict[name] = lambda: self.bodies_button_list_click(None)
button_dict[name] = lambda: self.search_bodies_button_list_click(None)
if hasattr(self, "bodies_button_list"):
self.bodies_button_list_close()
if hasattr(self, "search_bodies_button_list"):
self.search_bodies_button_list_close()
self.bodies_button_list = ButtonList(button_dict,
font=UrsinaConfig.CN_FONT,
button_height=1.5,
ignore_paused=True)
self.search_bodies_button_list = ButtonList(button_dict,
font=UrsinaConfig.CN_FONT,
button_height=1.5,
ignore_paused=True)
def on_reset_button_click(self):
paused = application.paused
......@@ -179,9 +198,9 @@ class ControlHandler(EventHandler):
elif key == 'left mouse down':
print(key)
elif key == 'y': # 寻找天体
if hasattr(self, "bodies_button_list"):
if self.bodies_button_list.enabled:
self.bodies_button_list_close()
if hasattr(self, "search_bodies_button_list"):
if self.search_bodies_button_list.enabled:
self.search_bodies_button_list_close()
return
self.on_searching_bodies_click()
elif key == 'o': # 重新开始
......
......@@ -21,6 +21,9 @@ from ursina import WindowPanel, InputField, Button, Slider, ButtonGroup, Panel,
class ControlUI(UiPanel):
"""
控制面板界面
"""
def component_init(self):
self.start_button_text = "●" # 》●▲○◎
self.pause_button_text = "〓" # 〓 || ‖
......
......@@ -14,5 +14,9 @@ class EventHandler:
self.handler_input_init()
def handler_input_init(self):
"""
@return:
"""
pass
......@@ -20,11 +20,22 @@ from simulators.ursina.ui.event_handler import EventHandler
class UiPanel(WindowPanel):
"""
界面面板类
"""
def __init__(self, handler: EventHandler, position=(0, 0), enabled=False, title=''):
"""
@param handler: 事件处理类
@param position: 界面位置
@param enabled: 是否显示
@param title: 标题
"""
self.components = self.component_init()
self.handler = handler
self.handler.ui = self
self.event_handler_init()
super().__init__(title=title, content=self.components, ignore_paused=True, color=color.rgba(0.0, 0.0, 0.0, 0.5))
self.y = position[1] # wp.panel.scale_y / 2 * wp.scale_y # center the window panel
......@@ -33,19 +44,29 @@ class UiPanel(WindowPanel):
self.after_component_init()
def after_component_init(self):
"""
组件初始化后运行
"""
pass
def component_init(self):
"""
组件初始化
"""
pass
def event_handler_init(self):
"""
事件处理初始化
@return:
"""
pass
def show_message(self, message, close_time=3):
"""
创建消息框
显示消息框
:param message: 消息内容
:param close_time: 定义关闭时间
:param close_time: 定义显示消息框关闭时间
:return:
"""
# 创建消息框
......
......@@ -16,6 +16,9 @@ from simulators.ursina.ursina_config import UrsinaConfig
class UiSlider(Slider):
"""
"""
def __init__(self, text, min=0.01, max=3, step=.01, default=1):
# Text.default_font = 'msyhl.ttc' # 'simsun.ttc'
super().__init__(text=text,
......@@ -36,6 +39,9 @@ class UiSlider(Slider):
class SwithButton(ButtonGroup):
"""
"""
def __init__(self, options, default, tooltips=None):
super().__init__(options, min_selection=1, default=default,
selected_color=color.rgba(0.1, 0.6, 0.1, 1.0), ignore_paused=True,
......@@ -53,6 +59,9 @@ class SwithButton(ButtonGroup):
class Buttons(ButtonGroup):
"""
"""
def __init__(self, options, default=None, tooltips=None):
min_selection = len(options)
super().__init__(options, min_selection=1, default=default,
......@@ -73,6 +82,9 @@ class Buttons(ButtonGroup):
class UiButton(Button):
"""
"""
def __init__(self, text, on_click):
super(UiButton, self).__init__(text=text, origin=(0, 0), y=2,
on_click=on_click, color=color.rgba(0.0, 0.0, 0.0, 0.5),
......
......@@ -11,15 +11,18 @@
class UrsinaEvent:
"""
ursina天体运行模拟器事件传递
"""
@staticmethod
def init():
if hasattr(UrsinaEvent, "on_reset_funcs"):
return
# 重启运行的订阅事件
UrsinaEvent.on_reset_funcs = []
# 搜索天体的订阅事件
UrsinaEvent.on_searching_bodies_funcs = []
# 应用运行的订阅事件
UrsinaEvent.on_application_run_callback = []
@staticmethod
......
......@@ -28,7 +28,10 @@ from ursina import EditorCamera, PointLight, SpotLight, AmbientLight, Directiona
from scenes.func import ursina_run
class WorldGrid(Entity): # Entity # 定义构造方法
class WorldGrid(Entity):
"""
创建一个宇宙网格对象
"""
def __init__(self):
super().__init__()
s = 100
......@@ -168,62 +171,62 @@ class UrsinaSimulator(Simulator):
# position=(0, 0, 0),
# rotation=(0, 0, 0))
def __add_glow(self, entity, intensity=2, light_color=color.white, attenuation=3):
"""
未用,保留代码
:param entity:
:param intensity:
:param light_color:
:param attenuation:
:return:
"""
lights = []
import math
for i in range(5):
glow_entity = Entity(parent=entity, model='sphere', color=color.rgba(1.0, 0.6, 0.2, 1),
scale=math.pow(1.03, i), alpha=0.2)
lights.append(glow_entity)
# 创建一个新的 Entity 对象,作为光晕的容器
# glow_entity = Entity(parent=entity, model='sphere', scale=entity.scale * 1.2)
# 创建 PointLight 对象,并设置它的属性
for i in range(2):
light = PointLight(parent=lights[0], intensity=intensity, color=light_color, attenuation=attenuation)
lights.append(light)
# 把 Entity 对象放到星星的后面,使得光晕看起来像是从星星发出来的
glow_entity.world_position = entity.world_position
glow_entity.world_parent = entity.parent
glow_entity.y += entity.scale_y * 0.1
glow_entity.depth_test = False
return lights
def create_fixed_star_lights(self, entity):
"""
创建恒星的发光的效果、并作为灯光源
:param entity:
:return:
"""
# 如果是恒星(如:太阳),自身会发光,则需要关闭灯光
entity.set_light_off()
lights = []
# 创建多个新的 Entity 对象,作为光晕的容器
for i in range(10):
glow_entity = Entity(parent=entity, model='sphere', color=color.rgba(1.0, 0.6, 0.2, 1),
scale=math.pow(1.03, i), alpha=0.1)
lights.append(glow_entity)
for i in range(2):
# 创建 PointLight 对象,作为恒星的灯光源
light = PointLight(parent=entity, intensity=10, range=10, color=color.white)
lights.append(light)
# light = DirectionalLight(shadows=True, direction=Vec3(0, 0, 1), color=color.white)
# light.look_at(Vec3(0, 0, -1))
# light = SpotLight(parent=entity,shadows=True, direction=Vec3(1,1,1), color=color.white)
return lights
# def __add_glow(self, entity, intensity=2, light_color=color.white, attenuation=3):
# """
# 未用,保留代码
# :param entity:
# :param intensity:
# :param light_color:
# :param attenuation:
# :return:
# """
# lights = []
# import math
# for i in range(5):
# glow_entity = Entity(parent=entity, model='sphere', color=color.rgba(1.0, 0.6, 0.2, 1),
# scale=math.pow(1.03, i), alpha=0.2)
# lights.append(glow_entity)
# # 创建一个新的 Entity 对象,作为光晕的容器
# # glow_entity = Entity(parent=entity, model='sphere', scale=entity.scale * 1.2)
# # 创建 PointLight 对象,并设置它的属性
# for i in range(2):
# light = PointLight(parent=lights[0], intensity=intensity, color=light_color, attenuation=attenuation)
# lights.append(light)
#
# # 把 Entity 对象放到星星的后面,使得光晕看起来像是从星星发出来的
# glow_entity.world_position = entity.world_position
# glow_entity.world_parent = entity.parent
# glow_entity.y += entity.scale_y * 0.1
# glow_entity.depth_test = False
# return lights
# def create_fixed_star_lights(self, entity):
# """
# 创建恒星的发光的效果、并作为灯光源
# :param entity:
# :return:
# """
#
# # 如果是恒星(如:太阳),自身会发光,则需要关闭灯光
# entity.set_light_off()
#
# lights = []
# # 创建多个新的 Entity 对象,作为光晕的容器
# for i in range(10):
# glow_entity = Entity(parent=entity, model='sphere', color=color.rgba(1.0, 0.6, 0.2, 1),
# scale=math.pow(1.03, i), alpha=0.1)
#
# lights.append(glow_entity)
# for i in range(2):
# # 创建 PointLight 对象,作为恒星的灯光源
# light = PointLight(parent=entity, intensity=10, range=10, color=color.white)
# lights.append(light)
#
# # light = DirectionalLight(shadows=True, direction=Vec3(0, 0, 1), color=color.white)
# # light.look_at(Vec3(0, 0, -1))
# # light = SpotLight(parent=entity,shadows=True, direction=Vec3(1,1,1), color=color.white)
#
# return lights
def run(self, dt, **kwargs):
......
......@@ -118,8 +118,10 @@ class Planet(Entity):
rotation=rotation # ,double_sided=True
)
if hasattr(self.body_view.body, "torus_stars"):
if hasattr(self.body_view.body, "torus_stars") or \
hasattr(self.body_view.body, "light_disable"):
# 星环小天体群(主要模拟小行星群,非一个天体)
# 或者灯光禁用
self.set_light_off()
self.double_sided = True
else:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册