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

Python超人-宇宙模拟器

上级
<p align="center">
<strong>Python太阳系、三体模拟器</strong>
</p>
# 介绍
《三体》是刘慈欣创作的长篇科幻小说,文中提到的三体问题比较复杂和无解。
该项目代码就是利用 Python 来模拟三体的运行,此项目代码完全共享,欢迎下载。
我们可以自己通过调整天体的初始坐标、质量和矢量速度等等参数来自定义各种场景来控制天体的运行效果。
# 三个模拟器效果图
<img src="https://gitcode.net/pythoncr/three_body_sim/-/raw/master/images/solar_system_3.png" width="80.7%">
<img src="https://gitcode.net/pythoncr/three_body_sim/-/raw/master/images/solar_system_1.png" width="40%">
<img src="https://gitcode.net/pythoncr/three_body_sim/-/raw/master/images/three_body_1.png" width="40%">
<img src="https://gitcode.net/pythoncr/three_body_sim/-/raw/master/images/three_body_3.png" width="40%">
<img src="https://gitcode.net/pythoncr/three_body_sim/-/raw/master/images/three_body_4.png" width="40%">
<img src="https://gitcode.net/pythoncr/three_body_sim/-/raw/master/images/bodies_run.gif" width="90%">
# 抖音课堂:
<img src="https://gitcode.net/pythoncr/three_body_sim/-/raw/master/images/douyin_x.jpg" width="40%">
# 课程下载
https://gitcode.net/pythoncr/three_body_sim
# 目录说明
**bodies** 天体类、包含太阳以及太阳系中的所有行星
**common** 公共库代码
**data** 构建天体的 JSON 数据
**scenes** 各种天体系统运行场景 **演示入口**
**textures** 天体纹理图片
**simulators** 天体系统运行模拟器
**images** 图片
# 安装 Python 库
```shell script
# 先安装基础包
pip install -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com ursina pyqt5 pyglet mayavi
```
# 支持三种模拟器
```shell script
# 进入当前代码的根目录
cd e:\three_body_sim\
SET PYTHONPATH=%CD%
# matplotlib 模拟器(支持动画和导出 gif 文件)
python simulators\mpl_simulator.py
# mayavi模拟器
python simulators\mayavi_simulator.py
# ursina模拟器
python simulators\ursina_simulator.py
```
# 模拟场景运行
```shell script
# 进入当前代码的根目录
cd e:\three_body_sim\
SET PYTHONPATH=%CD%
# 场景
# 从运行demo开始
python scenes/demo.py
# 三体场景
# 3个太阳、1个地球(效果1)
python scenes/three_body_01.py
# 3个太阳、1个地球(效果2)
python scenes/three_body_02.py
# 太阳系场景
# 以下展示的效果为太阳系真实的距离
# 由于宇宙空间尺度非常大,如果按照实际的天体大小,则无法看到天体,因此需要对天体的尺寸进行放大
python scenes/solar_system_1.py
# 以下展示的效果非太阳系真实的距离和大小
# 1、由于宇宙空间尺度非常大,如果按照实际的天体大小,则无法看到天体,因此需要对天体的尺寸进行放大
# 2、为了达到最佳的显示效果,对每个行星天体的距离进行了缩放
python scenes/solar_system_2.py
# 以下展示的效果非太阳系真实的距离和大小
# 1、由于宇宙空间尺度非常大,按照实际的大小无法看到行星天体,因此需要对天体的尺寸进行放大
# 2、为了达到最佳的显示效果,对每个行星天体的距离进行了缩放
# 3、加入了小行星的演示效果
python scenes/solar_system_3.py
# 太阳、地球运行效果
python scenes/sun_earth.py
# 太阳、地球、木星运行效果
python scenes/sun_earth_jupiter.py
```
# 免责声明
* 本项目开源代码和资料主要用于教学,任何直接或间接因使用我方的任何内容所导致的全部后果与我方无关,若使用者无法对使用我方内容后的任何后果负责,请不要使用我方的任何内容。若我方的任何内容侵犯了您的法律权益,请联系pythoncr@126.com,作者会第一时间删除侵权内容。
\ No newline at end of file
### 数字 0-9 上标小数字特殊符号
```python
# ⁰ ¹ ² ³ ⁴ ⁵ ⁶ ⁷ ⁸ ⁹
# 远日点距离:
# 近日点距离:
#  逃逸速度:
#  公转速度:
#  天体质量:
#  平均密度:
```
参考:
https://solarsystem.nasa.gov/solar-system/our-solar-system/overview/
https://trek.nasa.gov/mars/
https://trek.nasa.gov/moon/
https://www.jpl.nasa.gov/images
https://nasasearch.nasa.gov/search?affiliate=nasa&sort_by=&query=moon+map+texture
\ No newline at end of file
# 太阳系中的天体(不含太阳)
from bodies.body import Body
from bodies.earth import Earth
from bodies.jupiter import Jupiter
from bodies.mars import Mars
from bodies.mercury import Mercury
from bodies.neptune import Neptune
from bodies.pluto import Pluto
from bodies.saturn import Saturn
from bodies.uranus import Uranus
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
# 恒星(含太阳)
from bodies.fixed_stars.fixed_star import FixedStar
from bodies.sun import Sun
from bodies.fixed_stars.sirius import Sirius
from bodies.fixed_stars.stephenson_2_18 import Stephenson_2_18
from bodies.fixed_stars.rigel import Rigel
from bodies.fixed_stars.alcyone import Alcyone
from bodies.fixed_stars.antares import Antares
from bodies.fixed_stars.arcturus import Arcturus
from bodies.fixed_stars.betelgeuse import Betelgeuse
from bodies.fixed_stars.bellatrix import Bellatrix
from bodies.fixed_stars.aldebaran import Aldebaran
from bodies.fixed_stars.vy_canis_majoris import VYCanisMajoris
from bodies.fixed_stars.uy_scuti import UYScuti
from bodies.fixed_stars.eta_carinae import EtaCarinae
from bodies.fixed_stars.y_canum_venaticorum import YCanumVenaticorum
from bodies.fixed_stars.carinae_v382 import CarinaeV382
# -*- 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, AU
class Asteroid(Body):
"""
小行星
质量 (kg):~4.1×1010 kg
密度 :2.3±0.3 g/cm&sup3;
------------------------
小行星带距离太阳约2.17-3.64天文单位的空间区域内,聚集了大约50万颗以上的小行星。
这么多小行星能够被凝聚在小行星带中,除了太阳的引力作用以外,木星的引力也起着作用。
[小行星25143的数据如下]:
远日点距离: 253.520Gm (1.695AU)
近日点距离: 142.568Gm (0.953AU)
逃逸速度: 0.0002 km/s
 公转速度: 25.37 km/s
 天体质量: 4.1✕10¹⁰ kg
 平均密度: 2.3✕10³ kg/m³
"""
def __init__(self, name="小行星", mass=4.1e10,
init_position=[1.6 * AU, 0, 0],
init_velocity=[0, 25.37, 0],
texture="", size_scale=1.0, distance_scale=1.0):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 2.3e10,
"color": (179, 231, 255),
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale
}
super().__init__(**params)
def ignore_gravity(self, body):
"""
是否忽略引力
:param body:
:return:
"""
# 小行星只对恒星有引力,忽略其他行星的引力
if body.is_fixed_star:
return False
return True
if __name__ == '__main__':
asteroid = Asteroid()
print(asteroid)
# -*- 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, AU
class Asteroids(Body):
"""
小行星群
质量 (kg):~4.1×1010 kg
密度 :2.3±0.3 g/cm&sup3;
------------------------
小行星带距离太阳约2.17-3.64天文单位的空间区域内,聚集了大约50万颗以上的小行星。
这么多小行星能够被凝聚在小行星带中,除了太阳的引力作用以外,木星的引力也起着作用。
[小行星25143的数据如下]:
远日点距离: 253.520Gm (1.695AU)
近日点距离: 142.568Gm (0.953AU)
逃逸速度: 0.0002 km/s
 公转速度: 25.37 km/s
 天体质量: 4.1✕10¹⁰ kg
 平均密度: 2.3✕10³ kg/m³
"""
def __init__(self, name="小行星群", mass=1.9891e30,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
texture="asteroids.png", size_scale=1.0,
distance_scale=1.0,
rotation_speed=0.002, # 小行星绕太阳转一圈的时间在数年到几十年之间不等。
parent=None):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 1.408e3,
"color": (179, 231, 255),
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"parent": parent
}
super().__init__(**params)
# 环状星群
self.torus_stars = True
def ignore_gravity(self, body):
"""
是否忽略引力
:param body:
:return:
"""
# 小行星只对恒星有引力,忽略其他行星的引力
# if body.is_fixed_star:
return True
# return True
if __name__ == '__main__':
asteroids = Asteroids()
print(asteroids)
# -*- coding:utf-8 -*-
# title :天体基类
# description :天体基类(所有星体都继承了该类)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from abc import ABCMeta, abstractmethod
import json
import numpy as np
import math
from common.consts import AU
import copy
class Body(metaclass=ABCMeta):
"""
天体基类
"""
def __init__(self, name, mass, init_position, init_velocity,
density=5e3, color=(125 / 255, 125 / 255, 125 / 255),
texture=None, size_scale=1.0, distance_scale=1.0,
rotation_speed=None, parent=None, ignore_mass=False,
is_fixed_star=False, trail_color=None, show_name=False):
"""
天体类
:param name: 天体名称
:param mass: 天体质量 (kg)
:param init_position: 初始位置 (km)
:param init_velocity: 初始速度 (km/s)
:param density: 平均密度 (kg/m³)
:param color: 天体颜色(纹理图片优先)
:param texture: 纹理图片
:param size_scale: 尺寸缩放
:param distance_scale: 距离缩放
:param rotation_speed: 自旋速度(度/小时)
:param parent: 天体的父对象
:param ignore_mass: 是否忽略质量(如果为True,则不计算引力)
:param is_fixed_star: 是否为恒星
:param trail_color: 天体拖尾颜色(默认天体颜色)
"""
self.__his_pos = []
self.__his_vel = []
self.__his_acc = []
self.__his_reserved_num = 200
# 是否忽略质量(如果为True,则不计算引力)
self.ignore_mass = ignore_mass
if name is None:
name = getattr(self.__class__, '__name__')
self.name = name
self.__mass = mass
self.__init_position = None
self.__init_velocity = None
self.init_position = np.array(init_position, dtype='float32')
self.init_velocity = np.array(init_velocity, dtype='float32')
# self.__position = copy.deepcopy(self.init_position)
# self.__velocity = copy.deepcopy(self.init_velocity)
self.__density = density
self.__rotation_speed = rotation_speed
self.color = color
self.trail_color = color if trail_color is None else trail_color
self.texture = texture
self.size_scale = size_scale
self.distance_scale = distance_scale
# 初始化后,加速度为0,只有多个天体的引力才会影响到加速度
# km/s²
self.__acceleration = np.array([0, 0, 0], dtype='float32')
self.__record_history()
# 是否显示
self.appeared = True
self.parent = parent
self.__is_fixed_star = is_fixed_star
self.show_name = show_name
@property
def init_position(self):
"""
获取天体的初始位置(单位:km)
:return:
"""
return self.__init_position
@init_position.setter
def init_position(self, value):
"""
设置天体的初始位置(单位:km)
:param value:
:return:
"""
self.__init_position = np.array(value, dtype='float32')
self.__position = copy.deepcopy(self.__init_position)
@property
def init_velocity(self):
"""
获取天体的初始速度 (km/s)
:return:
"""
return self.__init_velocity
@init_velocity.setter
def init_velocity(self, value):
"""
设置天体的初始速度 (km/s)
:param value:
:return:
"""
self.__init_velocity = np.array(value, dtype='float32')
self.__velocity = copy.deepcopy(self.__init_velocity)
@property
def has_rings(self):
"""
是否为带光环的天体(土星为 True)
:return:
"""
return False
@property
def is_fixed_star(self):
"""
是否为恒星(太阳为 True)
:return:
"""
return self.__is_fixed_star
@is_fixed_star.setter
def is_fixed_star(self, value):
self.__is_fixed_star = value
@property
def position(self):
"""
获取天体的位置(单位:km)
:return:
"""
return self.__position
@position.setter
def position(self, value):
"""
设置天体的位置(单位:km)
:param value:
:return:
"""
self.__position = value
self.__record_history()
@property
def acceleration(self):
"""
获取天体的加速度(单位:km/s²)
:return:
"""
return self.__acceleration
@acceleration.setter
def acceleration(self, value):
"""
设置天体的加速度(单位:km/s²)
:param value:
:return:
"""
self.__acceleration = value
self.__record_history()
@property
def velocity(self):
"""
获取天体的速度(单位:km/s)
:return:
"""
return self.__velocity
@velocity.setter
def velocity(self, value):
"""
设置天体的速度(单位:km/s)
:param value:
:return:
"""
self.__velocity = value
self.__record_history()
def __append_history(self, his_list, data):
"""
追加每个位置时刻的历史数据
:param his_list:
:param data:
:return:
"""
# 如果历史记录为0 或者 新增数据和最后的历史数据不相同,则添加
if len(his_list) == 0 or \
np.sum(data == his_list[-1]) < len(data):
his_list.append(data.copy())
def __record_history(self):
"""
记录每个位置时刻的历史数据
:return:
"""
# 如果历史记录数超过了保留数量,则截断,只保留 __his_reserved_num 数量的历史
if len(self.__his_pos) > self.__his_reserved_num:
self.__his_pos = self.__his_pos[len(self.__his_pos) - self.__his_reserved_num:]
self.__his_vel = self.__his_vel[len(self.__his_vel) - self.__his_reserved_num:]
self.__his_acc = self.__his_acc[len(self.__his_acc) - self.__his_reserved_num:]
# 追加历史记录(位置、速度、加速度)
self.__append_history(self.__his_pos, self.position)
self.__append_history(self.__his_vel, self.velocity)
self.__append_history(self.__his_acc, self.acceleration)
# print(self.name, "his pos->", self.__his_pos)
def his_position(self):
"""
历史位置
:return:
"""
return self.__his_pos
def his_velocity(self):
"""
历史瞬时速度
:return:
"""
return self.__his_vel
def his_acceleration(self):
"""
历史瞬时加速度
:return:
"""
return self.__his_acc
@property
def mass(self):
"""
天体质量 (单位:kg)
:return:
"""
return self.__mass
@property
def rotation_speed(self):
"""
自旋速度(度/小时)
:return:
"""
return self.__rotation_speed
@rotation_speed.setter
def rotation_speed(self, value):
"""
自旋速度(度/小时)
:return:
"""
self.__rotation_speed = value
@property
def density(self):
"""
平均密度 (单位:kg/m³)
:return:
"""
return self.__density
@property
def volume(self):
"""
天体的体积(单位:km³)
"""
# v = m/ρ
# 体积(m³) = 质量(kg) / 密度(kg/m³)
# 体积(km³) = 体积(m³) / 1e9
v = self.mass / self.density / 1e9
return v
@property
def raduis(self):
"""
天体的半径(单位:km)
:return:
"""
# V = ⁴⁄₃πr³ -> r = pow((3V)/(4π),1/3)
return pow(3 * self.volume / (4 * math.pi), 1 / 3)
@property
def diameter(self):
"""
天体的直径(单位:km)
:return:
"""
return self.raduis * 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.position[0], self.position[1], self.position[2], self.velocity)
def ignore_gravity(self, body):
"""
是否忽略引力
:param body:
:return:
"""
return False
def position_au(self):
"""
获取天体的位置(单位:天文单位 A.U.)
:return:
"""
pos = self.position
pos_au = pos / AU
return pos_au
# def change_velocity(self, dv):
# self.velocity += dv
#
# def move(self, dt):
# self.position += self.velocity * dt
def reset(self):
"""
重新设置初始速度和初始位置
:return:
"""
self.position = copy.deepcopy(self.init_position)
self.velocity = copy.deepcopy(self.init_velocity)
# def kinetic_energy(self):
# """
# 计算动能(千焦耳)
# 表示动能,单位为焦耳j,m为质量,单位为千克,v为速度,单位为米/秒。
# ek=(1/2).m.v^2
# m(kg) v(m/s) -> j
# m(kg) v(km/s) -> kj
# """
# v = self.velocity
# return 0.5 * self.mass * (v[0] ** 2 + v[1] ** 2 + v[2] ** 2)
@staticmethod
def build_bodies_from_json(json_file):
"""
JSON文件转为天体对象
:param json_file:
:return:
"""
bodies = []
params = {}
from bodies import FixedStar
with open(json_file, "r", encoding='utf-8') as read_content:
json_data = json.load(read_content)
for body_data in json_data["bodies"]:
try:
body_data = Body.exp(body_data) # print(body_data)
except Exception as e:
err_msg = f"{json_file} 格式错误:" + str(e)
raise Exception(err_msg)
if "is_fixed_star" in body_data:
if body_data["is_fixed_star"]:
body_data.pop("is_fixed_star")
body = FixedStar(**body_data)
else:
body = FixedStar(**body_data)
bodies.append(body)
if "params" in json_data:
params = json_data["params"]
# print(body.position_au())
return bodies, params
@staticmethod
def exp(body_data):
"""
进行表达式分析,将表达式改为eval执行后的结果
:param body_data:
:return:
"""
#
for k in body_data.keys():
v = body_data[k]
if isinstance(v, str):
if v.startswith("$exp:"):
exp = v[5:]
body_data[k] = eval(exp)
elif isinstance(v, list):
for idx, item in enumerate(v):
if isinstance(item, str):
if item.startswith("$exp:"):
exp = item[5:]
v[idx] = eval(exp)
return body_data
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("params:", params)
for body in bodies:
print(body)
# -*- 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="戴森球", 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())
# -*- 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, AU
class Earth(Body):
"""
地球
------------------------
转轴倾角: 23.44°
自转周期: 23.93 小时,自转角速度约为 15 度/小时
远日点距离: 152097701 km
近日点距离: 147098074 km
 逃逸速度: 11.186 km/s
 公转速度: 29.79 km/s
 天体质量: 5.97237✕10²⁴ kg
 平均密度: 5507.85 kg/m³
"""
def __init__(self, name="地球", mass=5.97237e24,
init_position=[1.12 * AU, 0, 0],
init_velocity=[0, 29.79, 0],
texture="earth1.jpg", size_scale=1.0, distance_scale=1.0,
rotation_speed=15, ignore_mass=False, trail_color=None):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 5507.85,
"color": (1, 89, 162),
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass,
"trail_color": trail_color
}
super().__init__(**params)
if __name__ == '__main__':
earth = Earth()
print(earth)
1、天狼星,天狼星A是一颗散发着蓝色光芒的蓝矮星
距离地球约8.6光年。它比我们太阳就大很多,直径大约是太阳的1.78倍,体积可以容纳下5个太阳。
中文名天狼星外文名Sirius别 名α Canis Majoris A/ α CMa A分
类A:主序星 B:白矮星质 量A:2.063 ± 0.023 M⊙ B:1.018 ± 0.011 M⊙
平均密度B:10^8 ~ 10^10 kg/m³
直 径A:1.711 D⊙ B:0.0084 ± 0.03 D⊙
表面温度A:9940 K B:25000 ± 200 K逃逸速度671 km/s视星等A:-1.47 B:+8.44 System:-1.46绝对星等A:+1.42 B:+11.18自转周期44.5天赤 经System:06h 45m 08.91728s赤 纬System:-16° 42′ 58.0171″距地距离System:8.60 ± 0.04 ly(2.64 ± 0.01 pc)半长轴7.4957 ± 0.0025″离心率0.59142 ± 0.00037公转周期50.1284 ± 0.0043 yr平近点角149.161 ± 0.075°轨道倾角136.336 ± 0.040°升交点经度45.40 ± 0.071°光谱型A:A0mA1Va B:DA2U-B 色指数A:-0.05 B:-1.04B-V 色指数A:+0.00 B:-1.03光 度A:25.4 L☉ B:0.056 L☉
2、北河三,它是一颗进入到生命末期的橙巨星
距离地球约有33光年。虽然北河三的质量只有太阳的1.86倍左右,但是他的直径却是太阳的8.3倍,体积足以容纳下77个太阳。
3、参宿七,它距离地球约863光年,是一颗超级大的蓝超巨星
这颗恒星是天空中第7亮星,位于我们所熟悉的猎户座内,质量比太阳要大得多,大约是太阳的21倍,直径为9700万千米,是太阳的77倍,体积能容纳下40万个太阳。
4、参宿四,参宿四是一个红超巨星
他目前已经进入到了生命末期最后时刻,即将发生超新星爆发,因此体积膨胀的超级巨大,直径达到了13亿千米,可以容纳7亿个太阳,质量大约是太阳的15倍。如果将它放入我们太阳系的中心,将接近木星轨道。不过这依然不是最大。
5、大犬座vY,位于大犬座的红特超巨星
比参宿四要大很多,他的质量虽然说仅是太阳的17倍左右,但是直径却极为庞大,是太阳的1400倍,如果将大犬座VY放在太阳系中心,它的边缘将会超过木星轨道。
6、盾牌座UY,盾牌座UY是一颗位于盾牌座的红超巨星
它的直径长达23亿千米,是太阳的1708倍,体积可以容纳下50亿个太阳。如果把它放在太阳系的中心,体积将完全吞没木星轨道并接近土星。
7、史蒂文斯2-18,
史蒂文斯2-18是目前已知观测的恒星当中,发现体积最大的恒星,他位于地球2万光年外的一个疏散星团内,直径达到了惊人的29亿千米,大约是太阳的2150倍,如果以光速环绕史蒂文森2-18赤道一周,也需要8.5小时。它的体积能够足容纳下100亿颗太阳,如果我们将史蒂文森2-18放入太阳系的中心,外围将会延伸到土星轨道,可想而知史蒂文森2-18有多么庞大!
?大角星
请从 NASA 官网上获取以下恒星的信息:
天狼星A,参宿五,昴宿六,大角星,毕宿五,参宿七,猎犬座Y,海山二,心宿二,船底座V382,参宿四,大犬座VY,盾牌座 UY,史蒂文森2-18
恒星信息格式如下:
昴宿六 (Alcyone)
质量:太阳质量的6倍 (1.193e+31 kg)
半径:太阳半径的9倍 (6.613e+06 km)
直径: 太阳直径的9倍 (1.323e+07 km)
密度: kg/m³
自转: 度/小时(自转角速度)
# -*- coding:utf-8 -*-
# title :参宿七
# description :参宿七
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import FixedStar
from common.consts import MO
class Alcyone(FixedStar):
"""
昴宿六 (Alcyone)
--------------- 维基百科 ---------------
昴宿六
观测资料
观测资料
历元 J2000
星座 金牛座
星官 昴宿 昴
赤经 03h 47m 29.077s
赤纬 24° 06′ 18.49″
视星等(V) 2.87
特性
光谱分类 B7IIIe
U−B 色指数 −0.34
B−V 色指数 −0.09
天体测定
径向速度 (Rv) 5.40 km/s
自行 (μ) 赤经:19.34 ± 0.39 mas/yr
赤纬:-43.67 ± 0.33 mas/yr
视差 (π) 8.09 ± 0.42 mas
距离 440 ly
绝对星等 (MV) -2.8
详细资料
质量 6 M☉
半径 9 R☉
亮度 2,100 L☉
温度 13,000 K
自转速度 (v sin i) 149 km/s
昴宿六(η Tau/金牛座η)西方星名为Alcyone,是一个在金牛座的聚星系统。
距离太阳大约440光年,为昴宿星团中最明亮的一颗恒星,该星团很年轻,年龄大约1亿年。
还有数颗暗淡的恒星很靠近昴宿六,很可能都是星团的成员。
神话
西方星名Alcyone是由希腊神话来的,即阿尔克俄涅,她是阿特拉斯和普勒俄涅的七个女儿之一。
恒星系统
双星和聚星星表列出了它的3个伴星:
B是金牛座24,视星等6.28的A0主序星,距离117";
C是金牛座V647,是盾牌座δ型变星;
D是视星等9.15的F3主序星。
金牛座V647的变光幅度在+8.25到+8.30,周期1.13小时。
华盛顿双星目录列出了4个更暗的伴星,全都在11等以下,另外也表示伴星D本身也是个联星,2个几乎一样的成员分离0.30"。
主星,昴宿六A,包含了3个成员,最亮的是蓝白色的B型巨星,和其他昴星团的亮星相似。
视星等+2.87(绝对星等-2.8),将近9太阳半径。
大约13,000K的表面温度使他有2100倍太阳光度。
光谱类型B7IIIe代表在光谱里有发射线。像众多的Be星,昴宿六A自转速度是很高的149km/s,造成了环绕恒星赤道气体盘。
最近的伴星质量非常低,并且距离小于1毫角秒,轨道周期可能略大于4天。
另外一个恒星大约有蓝巨星质量的一半,分离0.031角秒,大约是太阳到木星的距离,轨道周期大约830天。
------------------------
== 太阳参数 ==
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="昴宿六", mass=6 * MO,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(0xBB, 0xAA, 0xFF),
texture="fixed_star.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.45, ignore_mass=False):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 9.85333138941539,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass
}
super().__init__(**params)
if __name__ == '__main__':
fixed_star = Alcyone()
print(fixed_star)
fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=9.5)
# -*- coding:utf-8 -*-
# title :大角星
# description :大角星
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import FixedStar
from common.consts import MO
class Aldebaran(FixedStar):
"""
毕宿五 (Aldebaran)
--------------- 维基百科 ---------------
观测资料
历元 J2000.0
星座 金牛座
星官 毕(毕宿)
赤经 04h 35m 55.23907s[1]
赤纬 +16° 30′ 33.4885″[1]
视星等(V) 0.75–0.95[2]
特性
演化阶段 红巨星分支[3]
光谱分类 K5+ III[4]
视星等 (J) −2.095[5]
U−B 色指数 +1.92[6]
B−V 色指数 +1.44[6]
变星类型 LB[2]
天体测定
径向速度 (Rv) +54.26±0.03[7] km/s
自行 (μ) 赤经:63.45±0.84[1] mas/yr
赤纬:−188.94±0.65[1] mas/yr
视差 (π) 49.97 ± 0.75[8] mas
距离 65.3 ± 1 ly
(20 ± 0.3 pc)
绝对星等 (MV) −0.641±0.034[8]
详细资料
质量 1.16±0.07[9] M☉
半径 44.13±0.84[10] R☉
表面重力 (log g) 1.45±0.3[11]
亮度 439±17[12] L☉
温度 3,900±50[11] K
金属量 [Fe/H] −0.33±0.1[11] dex
自转 520 days[13]
自转速度 (v sin i) 3.5±1.5[11] km/s
年龄 6.4Gyr[9] Gyr
------------------------
== 太阳参数 ==
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="毕宿五", mass=1.16 * MO,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(250, 195, 47),
texture="fixed_star.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.35, ignore_mass=False):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 0.019004605622458228,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass
}
super().__init__(**params)
if __name__ == '__main__':
fixed_star = Aldebaran()
print(fixed_star)
fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=44.13)
# -*- coding:utf-8 -*-
# title :心大星
# description :心大星
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import FixedStar
from common.consts import MO
class Antares(FixedStar):
"""
心宿二 (Antares)
--------------- 维基百科 ---------------
观测资料
历元 J2000
星座 天蝎座
星官 心 (心宿)
赤经 16h 29m 24s[1]
赤纬 −26° 25′ 55″[1]
视星等(V) +0.96[2]
特性
光谱分类 M1.5Iab-b + B2.5V[3]
U−B 色指数 +1.34[2]
B−V 色指数 +1.83[2]
变星类型 LC[4]
天体测定
径向速度 (Rv) −3.4[5] km/s
自行 (μ) 赤经:−12.11[1] mas/yr
赤纬:−23.30[1] mas/yr
视差 (π) 5.89 ± 1.00[1] mas
距离 大约550 ly
(大约170 pc)
绝对星等 (MV) −5.28
详细资料
A
质量 15.5[6] M☉
半径 680[6] R☉
表面重力 (log g) 0.1[6]
亮度 62,300[7] L☉
温度 3400 ± 200[7] K
自转速度 (v sin i) 20[8] km/s
B
质量 6-8 M☉
半径 4 R☉
温度 18,500[7] K
------------------------
== 太阳参数 ==
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="心宿二A", mass=15.5 * MO,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(249, 198, 83),
texture="fixed_star.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.25, ignore_mass=False):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 6.940769387339728e-05,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass
}
super().__init__(**params)
self.glows = 6
if __name__ == '__main__':
fixed_star = Antares()
print(fixed_star)
fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=680)
# -*- coding:utf-8 -*-
# title :大角星
# description :大角星
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import FixedStar
from common.consts import MO
class Arcturus(FixedStar):
"""
大角星 (Arcturus)
--------------- 维基百科 ---------------
观测资料
历元 J2000
星座 牧夫座
星官 苍龙亢宿大角
赤经 14h 15 m 39.7s
赤纬 +19° 10' 56"
视星等(V) −0.04
特性
光谱分类 K1.5 IIIpe
U−B 色指数 1.22
B−V 色指数 1.24
变星类型 Suspected
天体测定
径向速度 (Rv) −5.2 km/s
自行 (μ) 赤经:−1093.45 mas/yr
赤纬:−1999.40 mas/yr
视差 (π) 88.78 ± 0.68 mas
距离 36.7 ± 0.3 ly
(11.26 ± 0.09 pc)
绝对星等 (MV) −0.38
详细资料
质量 1.10 ± 0.06[1] M☉
半径 25.7 ± 0.3[2] R☉
亮度 180-210[3] L☉
温度 4,300[4] K
金属量 20–50% Sun
年龄 > 4.6 × 109 年
------------------------
== 太阳参数 ==
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="大角星", mass=1.1 * MO,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(254, 218, 185),
texture="fixed_star.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.4, ignore_mass=False):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 0.09124224657404181,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass
}
super().__init__(**params)
if __name__ == '__main__':
fixed_star = Arcturus()
print(fixed_star)
fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=25.7)
# -*- coding:utf-8 -*-
# title :大角星
# description :大角星
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import FixedStar
from common.consts import MO
class Bellatrix(FixedStar):
"""
参宿五 (Bellatrix)
--------------- 维基百科 ---------------
观测资料
历元 J2000
星座 猎户座
星官 参(参宿,白虎)
赤经 05h 25m 07.86325s[1]
赤纬 +06° 20′ 58.9318″[1]
视星等(V) 1.64[2] (1.59 - 1.64[3])
特性
光谱分类 B2 III[4]
U−B 色指数 –0.86[2]
B−V 色指数 –0.21[2]
变星类型 ?[3]
天体测定
径向速度 (Rv) +18.2[5] km/s
自行 (μ) 赤经:–8.11[1] mas/yr
赤纬:–12.88[1] mas/yr
视差 (π) 12.92 ± 0.52[1] mas
距离 250 ± 10 ly
(77 ± 3 pc)
绝对星等 (MV) −2.78[6]
详细资料
质量 8.6[7] M☉
半径 5.75[8] R☉
表面重力 (log g) 3.60[9]
亮度 9,211[8] L☉
温度 22000[9] K
金属量 [Fe/H] –0.07[8] dex
自转速度 (v sin i) 46±8[9] km/s
年龄 25.2[7] Myr
------------------------
== 太阳参数 ==
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="参宿五", mass=8.6 * MO,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(122, 187, 255),
texture="fixed_star.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.5, ignore_mass=False):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 63.69,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass
}
super().__init__(**params)
if __name__ == '__main__':
fixed_star = Bellatrix()
print(fixed_star)
fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=5.75)
# -*- coding:utf-8 -*-
# title :大角星
# description :大角星
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import FixedStar
from common.consts import MO
class Betelgeuse(FixedStar):
"""
参宿四 (Betelgeuse)
--------------- 维基百科 ---------------
观测资料
历元 J2000.0
星座 猎户座
星官 参宿
赤经 05h 55m 10.3053s[1]
赤纬 +07° 24′ 25.426″[1]
视星等(V) 0.42[1](0.3 to 1.2)
特性
光谱分类 M2Iab(红超巨星)[1]
U−B 色指数 2.06[2]
B−V 色指数 1.85(橙红)[2]
变星类型 SR c (半规则)[1]
天体测定
径向速度 (Rv) +21.91[1] km/s
自行 (μ) 赤经:24.95 ± 0.08[3] mas/yr
赤纬:9.56 ± 0.15[3] mas/yr
视差 (π) 5.07 ± 1.10[3] mas
距离 643 ± 146 [3] ly
(197 ± 45 [3] pc)
绝对星等 (MV) −6.05[4]
详细资料
质量 ~18–19[5] M☉
半径 ~1180[6] R☉
表面重力 (log g) -0.5[7]
亮度 ~140,000[8] L☉
温度 3,500[7][9] K
金属量 0.05 Fe/H[10]
自转 5 km/s[9]
年龄 ~1.0×107 [5] 年
其他命名
参宿四,α Ori,58 Ori,HR 2061, BD +7° 1055, HD 39801, FK5 224, HIP 27989, SAO 113271, GC 7451, CCDM J05552+0724AP, AAVSO 0549+07
------------------------
== 太阳参数 ==
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="参宿四", mass=19 * MO,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(254, 162, 1),
texture="fixed_star.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.24, ignore_mass=False):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 1.6282093105916417e-05,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass
}
super().__init__(**params)
if __name__ == '__main__':
fixed_star = Betelgeuse()
print(fixed_star)
fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=1180)
# -*- coding:utf-8 -*-
# title :船底座V382
# description :船底座V382
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import FixedStar
from common.consts import MO
class CarinaeV382(FixedStar):
"""
船底座V382(CarinaeV382)
--------------- 维基百科 ---------------
船底座V382
V382 Carinae.jpg
船底座V382位于照片中央
观测资料
历元 J2000.0
星座 船底座
星官 近南极星区 海石
赤经 11h08m35.4s
赤纬 −58°58′30″
视星等(V) +3.93
特性
光谱分类 G0I-a0 var
U−B 色指数 +0.94
B−V 色指数 +1.23
变星类型 造父变星
天体测定
径向速度 (Rv) +7 km/s
自行 (μ) 赤经:−5.03 mas/yr
赤纬:2.09 mas/yr
视差 (π) 0.55 ± 0.54 mas
距离 > 5900 ly
(> 1800 pc)
绝对星等 (MV) −7.36
详细资料
质量 39 M☉
半径 747 R☉
亮度 71500(可见光)L⊙/480000(辐射热)L⊙ L☉
温度 5550或4420 (以B-V) K
其他命名
x Carinae, HR 4337, HD 96918, CP−58°3189, FK5 1289, HIP 54463, SAO 238813, GC 15329
------------------------
== 太阳参数 ==
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="船底座V382", mass=39 * MO,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(255, 172, 40),
texture="fixed_star.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.26, ignore_mass=False):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 0.0001317362984479511,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass
}
super().__init__(**params)
if __name__ == '__main__':
fixed_star = CarinaeV382()
print(fixed_star)
fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=747)
# -*- coding:utf-8 -*-
# title :心宿二
# description :心宿二
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import FixedStar
from common.consts import MO
class EtaCarinae(FixedStar):
"""
海山二/船底座 (Eta Carinae)
--------------- 维基百科 ---------------
观测资料
历元 J2000
星座 船底座
星官 海山 (近南极星区)
赤经 10h 45m 03.6s[1]
赤纬 -59° 41′ 04″
视星等(V) 6.21 (-0.8–7.9)[1]
特性
光谱分类 B3-5Ia0/O7I(WC8)
U−B 色指数 -0.45
B−V 色指数 0.61
变星类型 高光度蓝变星 双星 或复合星
天体测定
径向速度 (Rv) −25.0[1] km/s
自行 (μ) 赤经:−7.6[1] mas/yr
赤纬:1.0[1] mas/yr
详细资料
质量 105-125/30[2] M☉
半径 278/12 R☉
亮度 5,150,000/292,000bolometric) L☉
温度 16500/38800 K
金属量 ?
自转 ?
年龄 ~ <3 × 106 年
------------------------
== 太阳参数 ==
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="海山二", mass=125 * MO,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(111, 140, 255),
texture="fixed_star.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.28, ignore_mass=False):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 0.008191779995598798,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass
}
super().__init__(**params)
if __name__ == '__main__':
fixed_star = EtaCarinae()
print(fixed_star)
fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=278)
# -*- 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
from common.consts import MO
from common.image_utils import gen_fixed_star_texture, find_texture_root_path
import os
class FixedStar(Body):
"""
恒星基类
------------------------
== 太阳参数 ==
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="恒星", mass=1 * MO,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(0xFF, 0xFF, 0xFF),
texture=None, size_scale=1.0, distance_scale=1.0,
rotation_speed=0.1, ignore_mass=False, density=1.408e3, trail_color=None,
texture_bright=None, texture_contrast=None):
if texture is None or texture == "fixed_star.png":
self.color = color
# bright=1.1, contrast=3.2
texture = self.gen_texture(texture, texture_bright, texture_contrast)
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": density,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass,
"trail_color": trail_color
}
super().__init__(**params)
self.light_on = True
self.glows = (12, 1.009, 0.08)
def gen_texture(self, texture, texture_bright, texture_contrast):
if texture is None:
return None
texture_path = find_texture_root_path()
if texture_path is None:
err_msg = "未找到纹理图片目录"
raise Exception(err_msg)
save_file = os.path.join(texture_path, "fixed_star_%s.png" % str(self.__class__.__name__).lower())
if os.path.exists(save_file):
return save_file
fixed_star_img = os.path.join(texture_path, texture)
gen_fixed_star_texture(self.color,
bright=texture_bright,
contrast=texture_contrast,
save_file=save_file,
fixed_star_img=fixed_star_img)
return save_file
@property
def is_fixed_star(self):
"""
恒星
:return:
"""
return True
def compare_with_sun(self):
from bodies import Sun
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 D☉ (%.4g km)" % (self.diameter / sun.diameter, self.diameter))
num_sun_volume = self.volume / sun.volume # 相当于多少个太阳体积
if num_sun_volume <= 10000:
print("体积: %.2f V☉ (%.4g km³)" % (num_sun_volume, self.volume))
elif num_sun_volume <= 100000000:
print("体积: %.2f万 V☉ (%.4g km³)" % (num_sun_volume / 10000, self.volume))
else:
print("体积: %.2f亿 V☉ (%.4g km³)" % (num_sun_volume / 100000000, self.volume))
def density_by_radius(self, raduis=None, num_sun_raduis=None):
"""
密度換算
@param raduis: 半径的长度(km)
@param num_sun_raduis: 多少个太阳半径
@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 __name__ == '__main__':
print(FixedStar())
# -*- coding:utf-8 -*-
# title :北河三
# description :北河三
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import FixedStar
from common.consts import MO
class Pollux(FixedStar):
"""
北河三 (Pollux)
--------------- 维基百科 ---------------
观测资料
历元 J2000.0
星座 双子座
星官 北河 (井宿)
赤经 07h 45m 18.94987s[1]
赤纬 +28° 01′ 34.3160″[1]
视星等(V) 1.14[2]
特性
演化阶段 巨星
光谱分类 K0III[3]
U−B 色指数 +0.86[2]
B−V 色指数 +1.00[2]
变星类型 Suspected[4]
天体测定
径向速度 (Rv) +3.23[5] km/s
自行 (μ) 赤经:–626.55[1] mas/yr
赤纬:–45.80[1] mas/yr
视差 (π) 96.54 ± 0.27[1] mas
距离 33.78 ± 0.09 ly
(10.36 ± 0.03 pc)
绝对星等 (MV) +1.08±0.02[6]
详细资料
质量 1.91±0.09[7] M☉
半径 8.8±0.1[8] R☉
表面重力 (log g) 2.685±0.09[8]
亮度 43[9] L☉
温度 4666±95[8] K
金属量 [Fe/H] –0.07 to +0.19[8] dex
自转 558 days[10]
自转速度 (v sin i) 2.8[11] km/s
年龄 724[12] Myr
其他命名
Beta Geminorum, 78 Geminorum, BD+28°1463, GCTP 1826.00, Gliese 286, HD 62509, HIP 37826, HR 2990, LFT 548, LHS 1945, LTT 12065, SAO 79666.[13]
------------------------
== 太阳参数 ==
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="北河三", mass=2 * MO,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(0xF5, 0xE8, 0xD5),
texture="fixed_star.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.1, ignore_mass=False):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 4.132231404958686,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass
}
super().__init__(**params)
if __name__ == '__main__':
fixed_star = Pollux()
print(fixed_star)
fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=8.8)
# -*- coding:utf-8 -*-
# title :北河三
# description :北河三
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import FixedStar
from common.consts import MO
class Procyon(FixedStar):
"""
南河三 (Procyon)
--------------- 维基百科 ---------------
南河三A/B
观测资料
历元 J2000
星座 小犬座
星官 南河(井宿,朱雀)
赤经 07h 39m 18.11950s[1]
赤纬 +05° 13′ 29.9552″[1]
视星等(V) 0.34[2] (A) / 10.7[3] (B)
特性
光谱分类 F5 IV–V[2] + DQZ[4]
U−B 色指数 +0.00[5]
B−V 色指数 +0.42[5]
变星类型 ?[6] (A)
天体测定
径向速度 (Rv) −3.2[7] km/s
自行 (μ) 赤经:−714.590[1] mas/yr
赤纬:−1036.80[1] mas/yr
视差 (π) 284.56 ± 1.26[1] mas
距离 11.46 ± 0.05 ly
(3.51 ± 0.02 pc)
绝对星等 (MV) 2.66/13.0[3]
详细资料
南河三A
质量 1.499±0.031[8] M☉
半径 2.048±0.025[2] R☉
表面重力 (log g) 3.96[2]
亮度 6.93[2] L☉
温度 6,530±50[2] K
金属量 [Fe/H] −0.05±0.03[2] dex
自转 23天[9]
自转速度 (v sin i) 3.16±0.50[2] km/s
年龄 1.87±0.13[8] Gyr
南河三B
质量 0.602±0.015[4] M☉
半径 0.01234±0.00032[4] R☉
表面重力 (log g) 8.0[4]
亮度 0.00049[10] L☉
温度 7,740±50[4] K
年龄 1.37[10] Gyr
轨道[11]
伴星 南河三B
绕行周期 (P) 40.82 yr
半长轴 (a) 4.3"
偏心率 (e) 0.407
倾斜角 (i) 31.1°
升交点黄经 (Ω) 97.3°
近心点 历元 (T) 1967.97
近心点幅角 (ω)
(secondary) 92.2°
其他命名
Elgomaisa, Algomeysa, Antecanis, α Canis Minoris, 10 Canis Minoris,GCTP 1805.00, HR 2943, BD+05°1739, HD 61421, LHS 233, GJ 280, HIP 37279, SAO 115756.[12]
------------------------
== 太阳参数 ==
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="南河三", mass=1.5 * MO,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(0xF5, 0xE8, 0xD5),
texture="fixed_star.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.1, ignore_mass=False):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 245.15024448281426,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass
}
super().__init__(**params)
if __name__ == '__main__':
fixed_star = Procyon()
print(fixed_star)
fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=2.05)
# -*- coding:utf-8 -*-
# title :猎户座一等星
# description :猎户座一等星
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import FixedStar
from common.consts import MO
class Rigel(FixedStar):
"""
参宿七 (Rigel)
--------------- 维基百科 ---------------
观测资料
历元 J2000.0
星座 猎户座
星官 参(参宿)
赤经 05h 14m 32.30s
赤纬 −08° 12' 06"
视星等(V) 0.12/6.80
特性
光谱分类 B8Ia
U−B 色指数 −0.66
B−V 色指数 −0.03
变星类型 Slightly irregular
天体测定
径向速度 (Rv) 20.7 km/s
自行 (μ) 赤经:1.87 mas/yr
赤纬:−0.56 mas/yr
视差 (π) 3.90 ± 0.81 mas
距离 大约800 ly
(大约260 pc)
绝对星等 (MV) −6.98
详细资料
质量 18 M☉
半径 74-78 R☉
亮度 53,800(126,000)(bolometric) L☉
温度 11,400 K
其他命名
β Orionis, 19 Ori, Algebar; Elgebar, HD 34085, HR 1713, HIP 24436, SAO 131907
------------------------
== 太阳参数 ==
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="参宿七", mass=18 * MO,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(200, 200, 255),
texture="fixed_star.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.33, ignore_mass=False):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 0.053406159915035785,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass
}
super().__init__(**params)
self.glows = 7
if __name__ == '__main__':
fixed_star = Rigel()
print(fixed_star)
fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=78)
# -*- coding:utf-8 -*-
# title :天狼星
# description :天狼星
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import FixedStar
from common.consts import MO
class Sirius(FixedStar):
"""
天狼星A (Sirius A)
质量:太阳质量的2.02倍 (3.994e+30 kg)
半径:太阳半径的1.711倍 (9.529e+05 km)
直径:太阳直径的1.423倍 (2.386e+06 km)
密度: 1.590 kg/m³
自转: 16.7 度/小时
--------------- 维基百科 ---------------
天狼星A/B
(大犬座α)
Sirius A / B
Position Alpha Cma.png
天狼星的位置
观测资料
历元 J2000.0 (ICRS)
星座 大犬座
星官 天狼 (井宿)
赤经 06h 45m 08.9173s[1][2]
赤纬 −16° 42′ 58.017″[1][2]
视星等(V) −1.47 (A)[1]/ 8.30 (B)[3]
特性
光谱分类 A1V (A)[1]/ DA2 (B)[3]
U−B 色指数 −0.05 (A)[4]/ −1.04 (B)[3]
B−V 色指数 0.01 (A)[1]/ −0.03 (B)[3]
天体测定
径向速度 (Rv) −7.6[1] km/s
自行 (μ) 赤经:−546.05[1][2] mas/yr
赤纬:−1223.14[1][2] mas/yr
视差 (π) 379.21 ± 1.58[1] mas
距离 8.6 ± 0.04 ly
(2.64 ± 0.01 pc)
绝对星等 (MV) 1.42 (A)[5]/ 11.18 (B)[3]
轨道[6]
伴星 天狼星 B
绕行周期 (P) 50.09 yr
半长轴 (a) 7.56"
偏心率 (e) 0.592
倾斜角 (i) 136.5°
升交点黄经 (Ω) 44.6°
近心点 历元 (T) 1894.13
近心点幅角 (ω)
(secondary) 147.3°
详细资料
质量 2.02[7](A) / 0.978[7](B) M☉
半径 1.711[7](A) / 0.0084 ± 3%[8](B) R☉
表面重力 (log g) 4.33[9](A)/8.57[8](B)
亮度 25.4[7](A) /
0.026[10](B) L☉
温度 9,940[9](A) /
25,200[7](B) K
金属量 [Fe/H] =0.50[11](A)
自转 16 km/s[12](A)
年龄 2-3 × 108[7] 年
其他命名
System: α Canis Majoris, α CMa, 9 Canis Majoris, 9 CMa, HD 48915, HR 2491, BD -16°1591, GCTP 1577.00 A/B, GJ 244 A/B, LHS 219, ADS 5423, LTT 2638, HIP 32349.
B: EGGR 49, WD 0642-166.[1][13][14]
------------------------
== 太阳参数 ==
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="天狼星A", mass=2.02 * MO,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(0xFF, 0xFF, 0xFF),
texture="fixed_star.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.55, ignore_mass=False):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 568.8079963025574,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass
}
super().__init__(**params)
if __name__ == '__main__':
fixed_star = Sirius()
print(fixed_star)
fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=1.71)
# -*- coding:utf-8 -*-
# title :史蒂文森2-18
# description :史蒂文森2-18
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import FixedStar
from common.consts import MO
class Stephenson_2_18(FixedStar):
"""
史蒂文森2-18 (Stephenson 2-18)
质量:40.0 太阳质量
分类: 红超巨星
直径: 3005015000 km
半径: 2158R☉ [1]
https://baijiahao.baidu.com/s?id=1734063731226819203&wfr=spider&for=pc
这颗恒星的体积就更加夸张了,根据科学家的估计,这颗恒星的体积,是太阳的100亿倍。换一句话说,史蒂文森2-18的体积是盾牌座UY的两倍。
--------------- 维基百科 ---------------
史蒂芬森2-18
Stephenson 2-18 zoomed in, 2MASS survey, 2003.png
2MASS拍摄的史蒂芬森2-18与其母星团史蒂芬森2(左上)。
Credit: 斯特拉斯堡大学/CNRS (2003)
观测资料
历元 J2000
星座 盾牌座
星官
赤经 18h 39m 02.3709s[1]
赤纬 -06° 05′ 10.5357″[1]
视星等(V)
特性
演化阶段 红超巨星
光谱分类 ~M6[2]
视星等 (G) 15.2631±0.0092[1]
视星等 (J) 7.150[3]
视星等 (H) 4.698[3]
视星等 (K) 2.9[3]
天体测定
自行 (μ) 赤经:−3.045±0.511[1] mas/yr
赤纬:−5.950±0.480[1] mas/yr
视差 (π) −0.0081 ± 0.3120[1] mas
距离 18,900[4] ly
(5,800[4] pc)
详细资料
半径 2,150[5][a] R☉
亮度 437,000[5] (90,000[6] – 630,000[4][b]) L☉
温度 3,200[5] K
其他命名
史蒂芬2-18、史蒂芬森2DFK1、RSGC2-18、2MASS J18390238-0605106、IRAS 18363-0607
------------------------
== 太阳参数 ==
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="史蒂文森2-18", mass=40.0 * MO,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(198, 29, 3),
texture="fixed_star.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.2, ignore_mass=False):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 5.666922409347618e-06,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass,
"texture_bright": 3,
"texture_contrast": 4
}
super().__init__(**params)
self.glows = (12, 1.008, 0.1)
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)
# -*- coding:utf-8 -*-
# title :参宿七
# description :参宿七
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import FixedStar
from common.consts import MO
class UYScuti(FixedStar):
"""
盾牌座 UY (UY Scuti)
质量 7-10 M☉
半径 1708 R☉ (有争议)
https://baijiahao.baidu.com/s?id=1734063731226819203&wfr=spider&for=pc
单单一个盾牌座UY,就能够容纳6500万亿个地球,太阳也能放进去50亿个。前文所说的弹珠还不恰当,准确来说,在盾牌座UY面前,太阳比细菌还不如。
--------------- 维基百科 ---------------
盾牌座UY
盾牌座UY(影像中最亮恒星)周围有大量恒星。
由美国哥伦比亚大学拉瑟弗德天文台摄于2011年。
观测资料
历元 J2000.0
星座 盾牌座
星官
赤经 18h 27m 36.5334s[1]
赤纬 -12° 27′ 58.866″[1]
视星等(V) 9.0[1]
特性
光谱分类 M4Ia [1]
B−V 色指数 2.6[1]
变星类型 Semiregular[1]
天体测定
自行 (μ) 赤经:1.3[1] mas/yr
赤纬:-1.6[1] mas/yr
详细资料
质量 7-10 M☉
半径 ~755 R☉
亮度 86300 ~ 87100 [2] L☉
其他命名
V* UY Sct、BD-12 5055、IRC -10422、RAFGL 2162[1]
------------------------
== 太阳参数 ==
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="盾牌座UY", mass=10 * MO,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(255, 116, 0),
texture="fixed_star.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.22, ignore_mass=False):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 2.825784611529699e-06,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass
}
super().__init__(**params)
if __name__ == '__main__':
fixed_star = UYScuti()
print(fixed_star)
fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=1708)
# -*- coding:utf-8 -*-
# title :大犬座VY
# description :大犬座VY
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import FixedStar
from common.consts import MO
class VYCanisMajoris(FixedStar):
"""
T大犬座VY(VY Canis Majoris)
--------------- 维基百科 ---------------
VY Canis Majoris
Sun and VY Canis Majoris.svg
太阳与大犬座VY大小比较
观测资料
历元 J2000
星座 大犬座
星官
赤经 07h 22m 58.33s[1]
赤纬 −25° 46′ 03.17″[1]
视星等(V) 6.5 to 9.6[2]
7.9607[3]
特性
光谱分类 M3[1]-M5e Ia[4]
B−V 色指数 2.24[1]
变星类型 半规则变星[5]
天体测定
径向速度 (Rv) 49 ± 10[1] km/s
自行 (μ) 赤经:9.84[1] mas/yr
赤纬:0.75[1] mas/yr
视差 (π) 0.83 ± 0.1[6] mas
距离 大约3900 ly
(大约1200 pc)
详细资料
质量 ~30[7]-40[8] M☉
半径 ~1400左右[9] R☉
亮度 ~450,000[10][11] L☉
温度 ~3000[11] K
------------------------
== 太阳参数 ==
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="大犬座VY", mass=30 * MO,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(234, 90, 65),
texture="fixed_star.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.23, ignore_mass=False):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 1.5393586005830937e-05,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass,
"texture_bright": 2,
"texture_contrast": 3
}
super().__init__(**params)
if __name__ == '__main__':
fixed_star = VYCanisMajoris()
print(fixed_star)
fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=1400)
# -*- coding:utf-8 -*-
# title :参宿七
# description :参宿七
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import FixedStar
from common.consts import MO
class YCanumVenaticorum(FixedStar):
"""
猎犬座Y (Y Canum Venaticorum)
分 类: 红巨星,碳星,变星
--------------- 维基百科 ---------------
La Superba
Y Canum Venaticorum.jpg
天文程式 Celestia 中的猎犬座Y(右)与太阳(左)的体积比较。
观测资料
历元 J2000.0
星座 猎犬座
星官
赤经 12h 45m 07.83s
赤纬 +45° 26' 24.92"
视星等(V) +4.8 to +6.3
天体测定
自行 (μ) 赤经:-2.20 mas/yr
赤纬:13.05 mas/yr
视差 (π) 4.590 mas
距离 711 ± 113 ly
(218 ± 35 pc)
特性
光谱分类 C54J, C-N5, C-J4.5
变星类型 半规则变星
详细资料
质量 3 M☉
半径 215 R☉
亮度 4,400
(bolometric) L☉
温度 2,800 K
其他命名
Y Canum Venaticorum, HR 4846, HD 110914, BD+46°1817, FK5 1327, HIP 62223, SAO 44317, GC 17342
------------------------
== 太阳参数 ==
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="猎犬座Y", mass=3 * MO,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(255, 55, 18),
texture="fixed_star.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.3, ignore_mass=False):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 0.000425,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass
}
super().__init__(**params)
if __name__ == '__main__':
fixed_star = YCanumVenaticorum()
print(fixed_star)
fixed_star.compare_with_sun()
fixed_star.density_by_radius(num_sun_raduis=215)
# -*- 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, AU
class Jupiter(Body):
"""
木星
------------------------
转轴倾角: 3.13°
自转周期: 9.93 小时,自转角速度约为 36.2537 度/小时 = 360/(9.93)
远日点距离: 5.4588 天文单位
近日点距离: 4.9501 天文单位
逃逸速度: 59.5 km/s
 公转速度: 13.06 km/s
 天体质量: 1.8982✕10²⁷ kg(317.8 M⊕)
 平均密度: 1.326 g/cm³ -> -> 1.326✕10³ kg/m³
"""
def __init__(self, name="木星", mass=1.8982e27,
init_position=[5.2 * AU, 0, 0],
init_velocity=[0, 13.06, 0],
texture="jupiter1.jpg", size_scale=1.0, distance_scale=1.0,
rotation_speed=36.2537, ignore_mass=False, trail_color=None):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 1.326e3,
"color": (173, 121, 92),
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass,
"trail_color": trail_color
}
super().__init__(**params)
if __name__ == '__main__':
jupiter = Jupiter()
print(jupiter)
# -*- 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, AU
class Mars(Body):
"""
火星
------------------------
转轴倾角: 25.19°
自转周期: 24.62 小时,自转角速度约为 14.6223 度/小时 = 360/(24.62)
远日点距离: 1.666 天文单位
近日点距离: 1.382 天文单位
逃逸速度: 5.027 km/s
 公转速度: 24.13 km/s
 天体质量: 6.4171✕10²³
 平均密度: 3.9335 g/cm³ -> 3.9335✕10³ kg/m³
"""
def __init__(self, name="火星", mass=6.4171e23,
init_position=[1.5 * AU, 0, 0],
init_velocity=[0, 24.13, 0],
texture="mars.jpg", size_scale=1.0, distance_scale=1.0,
rotation_speed=14.6223, ignore_mass=False, trail_color=None):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 3.9335e3,
"color": (213, 97, 59),
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass,
"trail_color": trail_color
}
super().__init__(**params)
if __name__ == '__main__':
mars = Mars()
print(mars)
\ 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.body import Body, AU
class Mercury(Body):
"""
水星
------------------------
转轴倾角: 0.034°
自转周期: 58.65 地球日,自转角速度约为 0.2558 度/小时 = 360/(58.65*24)
远日点距离: 0.466697 天文单位
近日点距离: 0.307499 天文单位
逃逸速度: 4.25 km/s
 公转速度: 47.87 km/s
 天体质量: 3.3011✕10²³ kg
 平均密度: 5.427 g/cm³ -> 5.427×10³ kg/m³
"""
def __init__(self, name="水星", mass=3.3011e23,
init_position=[0.4 * AU, 0, 0],
init_velocity=[0, 47.87, 0],
texture="mercury.jpg", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.2558, ignore_mass=False, trail_color=None):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 5.427e3,
"color": (1, 89, 162),
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass,
"trail_color": trail_color
}
super().__init__(**params)
if __name__ == '__main__':
mercury = Mercury()
print(mercury)
# -*- 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, AU
from bodies import Earth
class Moon(Body):
"""
月球
------------------------
 自转周期: 27.32 地球日,自转角速度约为 0.5487 度/小时 = 360/(27.32*24)
距地距离约: 363104 至 405696 km
 逃逸速度: 2.4 km/s
 公转速度: 1.023 km/s + (地球)29.79 km/s
 天体质量: 7.342✕10²² kg
 平均密度: 3.344 g/cm³ -> 3.344✕10³ kg/m³
"""
def __init__(self, name="月球", mass=7.342e22,
init_position=[363104 + 1.12 * AU, 0, 0],
init_velocity=[0, 29.79 + 1.023, 0],
texture="moon.jpg", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.5487, ignore_mass=False, trail_color=None):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 3.344e3,
"color": (162, 162, 162),
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass,
"trail_color": trail_color
}
super().__init__(**params)
# def ignore_gravity(self, body):
# """
# 是否忽略引力
# :param body:
# :return:
# """
# # 月球只对地球有引力,忽略其他的引力
# if isinstance(body, Earth):
# return False
#
# return True
if __name__ == '__main__':
moon = Moon()
print(moon)
# -*- 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, AU
class Neptune(Body):
"""
海王星
------------------------
自转轴倾角: 28.32°
自转周期: 16.11 小时,自转角速度约为 22.3463 度/小时 = 360/(16.11)
远日点距离: 30.33 天文单位
近日点距离: 29.81 天文单位
逃逸速度: 23.5 km/s
 公转速度: 5.43 km/s
 天体质量: 1.0241✕10²⁶ kg
 平均密度: 1.638 g/cm³ -> 1.638×10³ kg/m³
"""
def __init__(self, name="海王星", mass=1.0241e26,
init_position=[30 * AU, 0, 0],
init_velocity=[0, 5.43, 0],
texture="neptune.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=22.3463, ignore_mass=False, trail_color=None):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 1.638e3,
"color": (93, 118, 203),
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass,
"trail_color": trail_color
}
super().__init__(**params)
if __name__ == '__main__':
neptune = Neptune()
print(neptune)
# -*- 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, AU
class Pluto(Body):
"""
冥王星
------------------------
转轴倾角: 119.591±0.014°
自转周期: 6.39 地球日,自转角速度约为 -2.3474 度/小时(逆时针自转) = 360/(6.39*24)
远日点距离: 49.305 天文单位(73.760 亿千米)
近日点距离: 29.658 天文单位(44.368 亿千米)
逃逸速度: 1.212 km/s
 公转速度: 4.7 km/s
 天体质量: 1.303✕10²² kg(±0.003)
 平均密度: 1.854 g/cm³(±0.006) -> 1.854×10³ kg/m³
"""
def __init__(self, name="冥王星", mass=1.303e22,
init_position=[40 * AU, 0, 0],
init_velocity=[0, 4.7, 0],
texture="pluto.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=-2.3474, ignore_mass=False, trail_color=None):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 1.854e3,
"color": (67, 28, 7),
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass,
"trail_color": trail_color
}
super().__init__(**params)
if __name__ == '__main__':
pluto = Pluto()
print(pluto)
# -*- 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, AU
class Saturn(Body):
"""
土星
------------------------
自转倾角: 26.73 度
自转周期: 10.66 小时,自转角速度约为 33.7711 度/小时 = 360/(10.66)
远日点距离: 10.1238 天文单位
近日点距离: 9.0412 天文单位
逃逸速度: 35.49 km/s
 公转速度: 9.64 km/s
 天体质量: 5.6834✕10²⁶ kg
 平均密度: 0.687 g/cm³ -> 0.687×10³ kg/m³
"""
def __init__(self, name="土星", mass=5.6834e26,
init_position=[10 * AU, 0, 0],
init_velocity=[0, 9.64, 0],
texture="saturn.jpg", size_scale=1.0, distance_scale=1.0,
rotation_speed=33.7711, ignore_mass=False, trail_color=None):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 0.687e3,
"color": (219, 189, 159),
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass,
"trail_color": trail_color
}
super().__init__(**params)
@property
def has_rings(self):
"""
土星带光环的天体
:return:
"""
return True
@property
def rings_color(self):
"""
土星光环的颜色
:return:
"""
return 173, 121, 92
if __name__ == '__main__':
saturn = Saturn()
print(saturn)
# -*- coding:utf-8 -*-
# title :太阳
# description :太阳
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import FixedStar
class Sun(FixedStar):
"""
太阳
------------------------
自转周期: 24.47 地球日,自转角速度约为 0.6130 度/小时 = 360/(24.47*24)
天体质量: 1.9891×10³⁰ kg
平均密度: 1.408×10³ kg/m³
"""
def __init__(self, name="太阳", mass=1.9891e30,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
color=(170, 98, 25),
texture="sun2.jpg", size_scale=1.0, distance_scale=1.0,
rotation_speed=0.6130, ignore_mass=False, trail_color=None):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 1.408e3,
"color": color,
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass,
"trail_color": trail_color
}
super().__init__(**params)
@property
def is_fixed_star(self):
"""
太阳为恒星
:return:
"""
return True
if __name__ == '__main__':
print(Sun())
# -*- 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, AU
class Uranus(Body):
"""
天王星
------------------------
转轴倾角: 97.77°
自转周期: 17.24 小时,自转角速度约为 -20.8816 度/小时(逆时针自转) = 360/(17.24)
远日点距离: 20.11 天文单位
近日点距离: 18.33 天文单位
逃逸速度: 21.3 km/s
 公转速度: 6.81 km/s
 天体质量: 8.681✕10²⁵ kg(±0.0013)
 平均密度: 1.27 g/cm³ -> 1.27×10³ kg/m³
"""
def __init__(self, name="天王星", mass=8.681e25,
init_position=[19 * AU, 0, 0],
init_velocity=[0, 6.81, 0],
texture="uranus.png", size_scale=1.0, distance_scale=1.0,
rotation_speed=-20.8816, ignore_mass=False, trail_color=None):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 1.27e3,
"color": (94, 124, 193),
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass,
"trail_color": trail_color
}
super().__init__(**params)
if __name__ == '__main__':
uranus = Uranus()
print(uranus)
# -*- 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, AU
class Venus(Body):
"""
金星
------------------------
轨道倾角: 3.39458 度
自转周期: 243 地球日,自转角速度约为 -0.0617 度/小时(逆时针自转) = 360/(243*24)
远日点距离: 0.728213 天文单位
近日点距离: 0.718440天文单位
逃逸速度: 10.36 km/s
 公转速度: 35 km/s
 天体质量: 4.8675✕10²⁴ kg
 平均密度: 5.24g/cm3 -> 5.24×10³ kg/m³
"""
def __init__(self, name="金星", mass=4.8675e24,
init_position=[0.72 * AU, 0, 0],
init_velocity=[0, 35, 0],
texture="venus.jpg", size_scale=1.0, distance_scale=1.0,
rotation_speed=-0.0617, ignore_mass=False, trail_color=None):
params = {
"name": name,
"mass": mass,
"init_position": init_position,
"init_velocity": init_velocity,
"density": 5.24e3,
"color": (173, 81, 5),
"texture": texture,
"size_scale": size_scale,
"distance_scale": distance_scale,
"rotation_speed": rotation_speed,
"ignore_mass": ignore_mass,
"trail_color": trail_color
}
super().__init__(**params)
if __name__ == '__main__':
venus = Venus()
print(venus)
# -*- coding:utf-8 -*-
# title :颜色工具类
# description :颜色工具类
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
def conv_to_vec4_color(colour: tuple, alpha=1):
"""
:param colour:
:param alpha:
:return:
"""
from ursina import Vec4
if len(colour) == 3:
return Vec4(colour[0], colour[1], colour[2], alpha * 255) / 255
elif len(colour) == 4:
return Vec4(colour[0], colour[1], colour[2], colour[3]) / 255
raise Exception("colour错误")
def adjust_brightness(color, target_brightness: float = 0.6):
"""
调整颜色的亮度到目标 target_brightness(确保亮度不超过 1.0)
:param color:
:param target_brightness:(确保亮度不超过 1.0)
:return:
"""
from ursina import Vec4
# 获取颜色的亮度值
brightness = color.x * 0.299 + color.y * 0.587 + color.z * 0.114
# 如果亮度值不够,增加亮度
if brightness < target_brightness:
# 调整 RGB 值,确保亮度不超过 1.0
r = min(color.x + (target_brightness - brightness), 1.0)
g = min(color.y + (target_brightness - brightness), 1.0)
b = min(color.z + (target_brightness - brightness), 1.0)
return Vec4(r, g, b, color.w)
else:
return color
def get_inverse_color(color):
"""计算 RGB 颜色的反色"""
r, g, b = color
if r + g + b <= 3:
inverse_r = 1.0 - r
inverse_g = 1.0 - g
inverse_b = 1.0 - b
else:
inverse_r = 255 - r
inverse_g = 255 - g
inverse_b = 255 - b
return inverse_r, inverse_g, inverse_b
# -*- coding:utf-8 -*-
# title :常量定义
# description :常量定义
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
"""
太阳质量是用于测量恒星或如星系类大型天体的质量单位。
它的大小等于太阳的总质量,大约 1.9891×10³⁰ 千克(一般取2.0×10³⁰ 千克)。
用单位符号即可表示为M⊙
"""
MO = 1.9891e30
"""
天文单位
"""
AU: float = 149597870.700
"""
万有引力常数
"""
G: float = 6.67e-11
"""
一小时多少秒
"""
SECONDS_PER_HOUR = 60 * 60
"""
半天多少秒
"""
SECONDS_PER_HALF_DAY = SECONDS_PER_HOUR * 12
"""
一天多少秒
"""
SECONDS_PER_DAY = SECONDS_PER_HOUR * 24
"""
一周多少秒
"""
SECONDS_PER_WEEK = SECONDS_PER_DAY * 7
"""
一月多少秒(按照30天算)
"""
SECONDS_PER_MONTH = SECONDS_PER_DAY * 30
"""
一年多少秒(按照365天算)
"""
SECONDS_PER_YEAR = SECONDS_PER_DAY * 365
# -*- coding:utf-8 -*-
# title :宇宙背景星空图片生成
# description :宇宙背景星空图片生成
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from PIL import Image
from random import randint
import time
# xSize = 2048
# ySize = 1024
xSize = 800
ySize = 60
# x = int(xSize / 2)
# y = int(ySize / 2)
# im = Image.new('RGBA', (xSize, ySize), (0, 0, 0, 0))
# im.getpixel((x, y))
# step = 2000
#
#
# def move():
# """随机生成并返回12345678个数字,分别代表 上右下左 右上 右下 左下 左上"""
# return randint(0, xSize - 1), randint(0, ySize - 1)
#
#
# def color():
# """在所在像素下点涂颜色"""
# global im, x, y
# add_color = 255
# src_str_list = im.load()
# data = src_str_list[x, y]
# data_list = list(data)
# # print(data)
# if data_list[0] < 255:
# data_list[0] += add_color
# data_list[1] += add_color
# data_list[2] += add_color
# # data = tuple(data_list)
# data = (255, 255, 255, 0)
# im.putpixel((x, y), data)
#
#
# def judge():
# """判断是否已经走到边界上"""
# global x, y, xSize, ySize
# b = int(xSize / 2)
# if x >= xSize - 1:
# x -= b
# if y >= ySize - 1:
# y -= b
# if x <= 1:
# x += b
# if y <= 0:
# y += b
#
#
# def draw():
# """根据move返回值来移动图片上的像素点"""
# global x, y, im
# x, y = move()
# color()
#
#
# for i in range(step):
# move()
# draw()
# judge()
# im.save(f'asteroids.png')
from PIL import Image, ImageDraw, ImageFont
image = Image.new(mode='RGBA', size=(xSize, ySize))
draw_table = ImageDraw.Draw(im=image)
for i in range(500):
x,y = randint(0, xSize - 1), randint(0, ySize - 1)
draw_table.point(xy=(x, y), fill='#FFFFFF')
image.show() # 直接显示图片
image.save('asteroids.png', 'PNG') # 保存在当前路径下,格式为PNG
image.close()
\ 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 PIL import Image
from common.consts import AU
import numpy as np
import random
def get_dominant_colors(infile, resize=(20, 20)):
"""
获取图片的主要颜色
:param infile:
:param resize:
:return:
"""
image = Image.open(infile)
# 缩小图片,否则计算机压力太大
small_image = image.resize(resize)
result = small_image.convert(
"P", palette=Image.ADAPTIVE, colors=10
)
# 10个主要颜色的图像
# 找到主要的颜色
palette = result.getpalette()
color_counts = sorted(result.getcolors(), reverse=True)
colors = list()
for i in range(min(10,len(color_counts))):
palette_index = color_counts[i][1]
dominant_color = palette[palette_index * 3: palette_index * 3 + 3]
colors.append(tuple(dominant_color))
return colors
def get_positions_velocitys(angles, velocity=1, radius=1, radius_offset=None, velocity_offset=None):
"""
以位置 (0, 0, 0)为中心,随机获取空间上的位置和公转方向的速度集合
(比如:获取大批小行星的位置)
:param angles: 参考中心位置(0, 0, 0)的角度集合
:param velocity: 速度
:param radius: 半径(距离中心位置(0, 0, 0)的距离)
:param radius_offset:在半径的基础上,随机偏移的值
:param velocity_offset:在速度的基础上,随机偏移的值
:return:
"""
angles = np.array(angles * np.pi)
if isinstance(radius_offset, float):
radius = radius + np.random.rand(len(angles)) * radius_offset
if isinstance(velocity_offset, float):
velocity = velocity + np.random.rand(len(angles)) * velocity_offset
pxs = radius * np.cos(angles)
pys = radius * np.sin(angles)
vys = velocity * np.cos(angles)
vxs = velocity * np.sin(angles)
# return pxs, pys, fxs, fys
return np.round(pxs, 2), np.round(pys, 2), -np.round(vxs, 2), np.round(vys, 2)
def calculate_distance(pos1, pos2=[0, 0, 0]):
"""
计算两点间的距离
:param pos1:
:param pos2:
:return:
"""
d = pow(pow(np.array(pos1[0]) - np.array(pos2[0]), 2) +
pow(np.array(pos1[1]) - np.array(pos2[1]), 2) +
pow(np.array(pos1[2]) - np.array(pos2[2]), 2), 1 / 2)
return d
if __name__ == '__main__':
print(calculate_distance([6, 8, 0], [3, 4, 0]))
# -*- coding:utf-8 -*-
# title :图片处理工具类
# description :图片处理工具类
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from PIL import Image, ImageColor, ImageEnhance, ImageStat
import math
import os
import numpy as np
import colorsys
rgb_to_hsv = np.vectorize(colorsys.rgb_to_hsv)
hsv_to_rgb = np.vectorize(colorsys.hsv_to_rgb)
def image_file_enhance(imageFilePath, bright, contrast, color, sharpness, saveFolderPath):
"""
图像增强之亮度、对比度与饱和度调整
:param imageFilePath: 图像文件路径
:param bright: 亮度
:param contrast: 对比度
:param color: 饱和度
:param sharpness: 清晰度
:param saveFolderPath: 结果保存路径
:return:
"""
imageFileName = os.path.basename(imageFilePath)
imageOriginal = Image.open(imageFilePath)
# 亮度调整
brightEnhancer = ImageEnhance.Brightness(imageOriginal)
imageBright = brightEnhancer.enhance(bright)
imageBrightFileName = "Bright-%0.2f_" % bright + imageFileName
imageBrightFilePath = os.path.join(saveFolderPath, imageBrightFileName)
imageBright.save(imageBrightFilePath)
# 对比度调整
contrastEnhancer = ImageEnhance.Contrast(imageOriginal)
imageContrast = contrastEnhancer.enhance(contrast)
imageContrastFileName = "Contrast-%0.2f_" % contrast + imageFileName
imageContrastFilePath = os.path.join(saveFolderPath, imageContrastFileName)
imageContrast.save(imageContrastFilePath)
# 饱和度调整
colorEnhancer = ImageEnhance.Color(imageOriginal)
imageColor = colorEnhancer.enhance(color)
imageColorFileName = "Color-%0.2f_" % color + imageFileName
imageColorFilePath = os.path.join(saveFolderPath, imageColorFileName)
imageColor.save(imageColorFilePath)
# 清晰度调整
SharpnessEnhancer = ImageEnhance.Sharpness(imageOriginal)
imageSharpness = SharpnessEnhancer.enhance(sharpness)
imageSharpnessFileName = "Sharpness-%0.2f_" % sharpness + imageFileName
imageSharpnessFilePath = os.path.join(saveFolderPath, imageSharpnessFileName)
imageSharpness.save(imageSharpnessFilePath)
return
def image_enhance(imageOriginal, bright=0, contrast=0, color=0, sharpness=0):
"""
图像增强之亮度、对比度与饱和度调整
:param imageFilePath: 图像文件路径
:param bright: 亮度
:param contrast: 对比度
:param color: 饱和度
:param sharpness: 清晰度
:param saveFolderPath: 结果保存路径
:return:
"""
image = imageOriginal
if bright > 0:
# 亮度调整
brightEnhancer = ImageEnhance.Brightness(image)
image = brightEnhancer.enhance(bright)
if contrast > 0:
# 对比度调整
contrastEnhancer = ImageEnhance.Contrast(image)
image = contrastEnhancer.enhance(contrast)
if color > 0:
# 饱和度调整
colorEnhancer = ImageEnhance.Color(image)
image = colorEnhancer.enhance(color)
if sharpness > 0:
# 清晰度调整
SharpnessEnhancer = ImageEnhance.Sharpness(image)
image = SharpnessEnhancer.enhance(sharpness)
return image
def shift_hue(arr, hout):
r, g, b, a = np.rollaxis(arr, axis=-1)
h, s, v = rgb_to_hsv(r, g, b)
h = hout
r, g, b = hsv_to_rgb(h, s, v)
arr = np.dstack((r, g, b, a))
return arr
def colorize_color(src_image, color):
h, s, v = rgb_to_hsv(*color)
# img_hsv = src_image.convert('HSV')
return colorize(src_image, h * 255)
def colorize(src_image, hue):
"""
Colorize PIL image `original` with the given
`hue` (hue within 0-360); returns another PIL image.
"""
img = Image.open(src_image)
img = img.convert('RGBA')
arr = np.array(np.asarray(img).astype('float'))
new_img = Image.fromarray(shift_hue(arr, hue / 360.).astype('uint8'), 'RGBA')
return new_img
# 图片背景透明化
def trans_png(src_image, alpha=100):
img = Image.open(src_image)
img = img.convert("RGBA")
datas = img.getdata()
newData = list()
for item in datas:
r = item[0] if item[0] < 255 else 255
b = item[1] if item[1] < 255 else 255
g = item[2] if item[2] < 255 else 255
newData.append((r, b, g, alpha))
img.putdata(newData)
return img
# 图片融合
def mix(img1, img2, coordinator=(0, 0)):
im = img1
mark = img2
layer = Image.new('RGBA', im.size, (0, 0, 0, 0))
layer.paste(mark, coordinator)
out = Image.composite(layer, im, layer)
return out
def create_image(width, height, color):
"""
创建指定大小和背景颜色的图片对象
参数:
width: 图片宽度
height: 图片高度
color: 背景颜色,可以是RGB元组或颜色名称字符串
返回:
Image对象
"""
# 创建图片对象
image = Image.new('RGB', (width, height), color)
return image
def rgb_to_hex(rgb):
r, g, b = rgb
hex_color = "#{:02X}{:02X}{:02X}".format(r, g, b)
return hex_color
def find_texture_root_path():
paths = [os.path.join('.', 'textures'), os.path.join('..', 'textures'), os.path.join('..', '..', 'textures')]
for path in paths:
if os.path.exists(path):
return path
return None
def find_texture(texture):
"""
尝试在多个路径下寻找纹理图片
:param texture: 纹理图片
:return: 纹理图片的路径
"""
if os.path.exists(texture):
return texture
paths = [os.path.join('.', 'textures'), os.path.join('..', 'textures'), os.path.join('..', '..', 'textures')]
for path in paths:
p = os.path.join(path, texture)
if os.path.exists(p):
return p
return ""
def gen_fixed_star_texture(color, save_file, fixed_star_img="fixed_star.png", bright=None, contrast=None):
bright = 1.1 if bright is None else bright
contrast = 3.2 if contrast is None else contrast
fixed_star_img = find_texture(fixed_star_img)
if fixed_star_img is None:
err_msg = "未找到纹理图片:" % fixed_star_img
raise Exception(err_msg)
trans_img = trans_png(fixed_star_img)
bg_img = create_image(trans_img.width, trans_img.height, color)
mixed = mix(bg_img, trans_img)
mixed = image_enhance(mixed, bright=bright, contrast=contrast)
mixed.save(save_file, 'PNG')
return mixed
def gray_to_mono(gray_img, color_val):
"""
将灰度图片转为单色图片,颜色值为 color_val
参数:
gray_img: 灰度图片,PIL Image对象
color_val: 单色值,RGB颜色值的元组,例如(255, 0, 0)表示红色
返回值:
单色图片,PIL Image对象
"""
# 创建一个白色图片,用于后续的alpha通道处理
white_img = Image.new('RGB', gray_img.size, (255, 255, 255))
# 将灰度图片转换为单色图片
mono_img = gray_img.convert('L').convert('RGBA')
# 用单色值替换灰色部分
for y in range(mono_img.height):
for x in range(mono_img.width):
r, g, b, a = mono_img.getpixel((x, y))
if r == g == b:
# 灰度颜色值范围:0~255
gray_val = r
# 将灰度值映射到颜色值范围内
color_r = int((gray_val / 255) * color_val[0])
color_g = int((gray_val / 255) * color_val[1])
color_b = int((gray_val / 255) * color_val[2])
mono_img.putpixel((x, y), (color_r, color_g, color_b, a))
# 将白色部分替换回去
alpha_img = white_img.convert('RGBA')
alpha_img.paste(mono_img, (0, 0), mono_img)
return alpha_img
if __name__ == '__main__':
# gray_img = Image.open("../textures/fixed_star.png")
# mono = gray_to_mono(gray_img, (0, 0, 255)).show()
# image_enhance(mono,bright=1,contrast=2).show()
gen_fixed_star_texture((198, 29, 3), "xxx.png",bright=2.2,contrast=3).show()
# fixed_star_img = find_texture("sun.png")
# colorize_color(fixed_star_img,(0,0xff,0xff)).show()
# def brightness(src_img):
# if isinstance(src_img, str):
# im = Image.open(src_img)
# else:
# im = src_img
# im = im.convert('L')
# stat = ImageStat.Stat(im)
# return stat.mean[0]
#
#
# def brightness(src_img):
# if isinstance(src_img, str):
# im = Image.open(src_img)
# else:
# im = src_img
# im = im.convert('L')
# stat = ImageStat.Stat(im)
# return stat.rms[0]
#
#
# def brightness(src_img):
# if isinstance(src_img, str):
# im = Image.open(src_img)
# else:
# im = src_img
# stat = ImageStat.Stat(im)
# r, g, b = stat.mean
# return math.sqrt(0.241 * (r ** 2) + 0.691 * (g ** 2) + 0.068 * (b ** 2))
#
#
# def brightness(src_img):
# if isinstance(src_img, str):
# im = Image.open(src_img)
# else:
# im = src_img
# stat = ImageStat.Stat(im)
# r, g, b = stat.rms
# return math.sqrt(0.241 * (r ** 2) + 0.691 * (g ** 2) + 0.068 * (b ** 2))
# def brightness(src_img):
# if isinstance(src_img, str):
# im = Image.open(src_img)
# else:
# im = src_img
# stat = ImageStat.Stat(im)
# gs = (math.sqrt(0.241 * (r ** 2) + 0.691 * (g ** 2) + 0.068 * (b ** 2)) for r, g, b in im.getdata())
# return sum(gs) / stat.count[0]
#
#
# def auto_adjust_contrast_brightness_xxx(src_img):
# """
# 自适应地调整图片的对比度和亮度。
#
# :param image_path: 图像路径。
# :return: 调整后的图像对象。
# """
# if isinstance(src_img, str):
# image = Image.open(src_img)
# else:
# image = src_img
# # 打开图像文件
# # image = Image.open(image_path)
#
# # 获取图像直方图
# histogram = image.histogram()
#
# # 计算直方图的最小和最大值
# min_value, max_value = 0, 255
# for i in range(256):
# if histogram[i] > 0:
# min_value = i
# break
# for i in range(255, -1, -1):
# if histogram[i] > 0:
# max_value = i
# break
#
# # 计算调整参数
# mid_value = 127.5
# contrast_factor = 127.5 / (max_value - min_value)
# brightness_factor = mid_value - contrast_factor * (min_value + max_value) / 2
#
# # 调整对比度和亮度
# contrast_enhancer = ImageEnhance.Contrast(image)
# contrast_image = contrast_enhancer.enhance(contrast_factor)
# brightness_enhancer = ImageEnhance.Brightness(contrast_image)
# brightness_image = brightness_enhancer.enhance(brightness_factor)
#
# return brightness_image
#
#
#
# def auto_adjust_contrast_brightness333(src_img):
# """
# 自适应地调整图片的对比度和亮度。
#
# :param image_path: 图像路径。
# :return: 调整后的图像对象。
# """
# if isinstance(src_img, str):
# image = Image.open(src_img)
# else:
# image = src_img
#
# # 计算图像平均亮度
# brightness = 0
# pixels = image.load()
# width, height = image.size
# for x in range(width):
# for y in range(height):
# r, g, b = pixels[x, y]
# brightness += 0.299 * r + 0.587 * g + 0.114 * b
# brightness /= width * height
#
# # 计算调整参数
# mid_value = 127.5
# contrast_factor = mid_value / brightness
# brightness_factor = mid_value - brightness * contrast_factor
#
# # 调整对比度和亮度
# contrast_enhancer = ImageEnhance.Contrast(image)
# contrast_image = contrast_enhancer.enhance(contrast_factor)
# brightness_enhancer = ImageEnhance.Brightness(contrast_image)
# brightness_image = brightness_enhancer.enhance(brightness_factor)
#
# return brightness_image
#
#
# def auto_adjust_contrast_brightness(src_img):
# """
# 自适应地调整图片的对比度和亮度。
#
# :param image_path: 图像路径。
# :return: 调整后的图像对象。
# """
# if isinstance(src_img, str):
# image = Image.open(src_img)
# else:
# image = src_img
#
# # 获取图像直方图
# histogram = image.histogram()
#
# # 计算直方图的最小和最大值
# min_value, max_value = 0, 255
# for i in range(256):
# if histogram[i] > 0:
# min_value = i
# break
# for i in range(255, -1, -1):
# if histogram[i] > 0:
# max_value = i
# break
#
# # 计算调整参数
# mid_value = 127.5
# contrast_factor = mid_value / (max_value - min_value)
# brightness_factor = mid_value - contrast_factor * (min_value + max_value) / 2
#
# # 调整对比度和亮度
# contrast_enhancer = ImageEnhance.Contrast(image)
# contrast_image = contrast_enhancer.enhance(contrast_factor)
# brightness_enhancer = ImageEnhance.Brightness(contrast_image)
# brightness_image = brightness_enhancer.enhance(brightness_factor)
#
# return brightness_image
# -*- coding:utf-8 -*-
# title :天体系统
# description :天体系统,多个天体就是一个系统
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# 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
class System(object):
"""
天体系统
"""
def __init__(self, bodies, max_distance=200 * AU):
"""
:param bodies:
: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)
def total_mass(self):
"""
总质量
:return:
"""
total_mass = 0.0
for body in self.bodies:
total_mass += body.mass
return total_mass
def __repr__(self):
return 'System({})'.format(self.bodies)
def center_of_mass(self):
"""
质心
:return:
"""
r = np.zeros(2)
for body in self.bodies:
r = body.mass * body.position
return r / self.total_mass()
def evolve(self, dt):
"""
:param dt:
:return:
"""
self.calc_bodies_acceleration()
for body in self.bodies:
# acceleration 加速度
body.velocity += body.acceleration * dt
# body.position += 0.5 * body.acceleration * (dt ** 2)
body.position += body.velocity * dt
def save_to_json(self, json_file_name, params=None):
"""
:param json_file_name:
:param params:
:return:
"""
import json
import os
# json_file = os.path.join("../data", json_file_name)
filed_names = ["name", "mass", "init_position", "init_velocity",
"density", "color", "texture",
"size_scale", "distance_scale", # "parent"
"rotation_speed", "ignore_mass", "is_fixed_star", "trail_color"]
bodies = []
for b in self.bodies:
body = {}
for filed_name in filed_names:
filed_value = getattr(b, filed_name)
if type(filed_value) is np.ndarray:
filed_value = filed_value.tolist()
body[filed_name] = filed_value
bodies.append(body)
data = {"bodies": bodies}
if params is not None:
data["params"] = params
json_str = json.dumps(data, indent=2, ensure_ascii=False, separators=(',', ': '))
with open(json_file_name, "w", encoding='utf-8') as f:
f.write(json_str)
def calc_bodies_acceleration(self):
"""
计算加速度
:return:
"""
def valid_body(body):
"""
判断是否为有效的天体
:param body:
:return:
"""
if not body.appeared: # 不显示
return False
# if self.max_distance > 0:
# # 超过了 max_distance 距离,则不显示,并消失
# if calculate_distance(body.position) > self.max_distance:
# body.appeared = False
# return False
return True
# self.bodies = list(filter(valid_body, self.bodies))
for body1 in self.bodies:
if body1.ignore_mass:
continue
if not valid_body(body1):
continue
acceleration = np.zeros(3)
for body2 in self.bodies:
if body2.ignore_mass:
continue
if self.max_distance > 0:
if calculate_distance(body1.position) > self.max_distance: # 超过了max_distance距离,则消失
body1.appeared = False
if calculate_distance(body2.position) > self.max_distance: # 超过了max_distance距离,则消失
body2.appeared = False
if not body1.appeared or not body2.appeared:
continue
if body1 is body2:
continue
elif body1.ignore_gravity(body2) or body2.ignore_gravity(body1):
continue
r = body2.position - body1.position
# G = 6.67e-11 # 万有引力常数
# m/s² = kg * m / m**3
# km/s² = kg * m / m**3 / 1e9
# acceleration = G * body2.mass * dx / (d ** 3)
acceleration += (G * body2.mass * r / np.linalg.norm(r) ** 3) / 1e9
body1.acceleration = acceleration
if __name__ == '__main__':
# body_sys = System([
# Sun(), # 太阳
# Mercury(), # 水星
# Venus(), # 金星
# Earth(), # 地球
# Mars(), # 火星
# Jupiter(), # 木星
# Saturn(), # 土星
# Uranus(), # 天王星
# 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"))
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)
{
"bodies": [
{
"name": "太阳",
"mass": 1.9891e+30,
"init_position": [
0.0,
0.0,
0.0
],
"init_velocity": [
0.0,
0.0,
0.0
],
"density": 1408.0,
"color": [
170,
98,
25
],
"texture": "sun2.jpg",
"size_scale": 20.0,
"distance_scale": 1.0,
"rotation_speed": 0.613,
"ignore_mass": false,
"is_fixed_star": true
},
{
"name": "地球",
"mass": 5.97237e+24,
"init_position": [
0.0,
-448793600.0,
0.0
],
"init_velocity": [
0.0,
38.0,
-1.0
],
"density": 5507.85,
"color": [
1,
89,
162
],
"texture": "earth1.jpg",
"size_scale": 1000.0,
"distance_scale": 1.0,
"rotation_speed": 15,
"ignore_mass": false,
"is_fixed_star": false
}
],
"params": {
"dt": 2592000,
"position": [
0,
149597870.7,
-448793612.09999996
]
}
}
\ No newline at end of file
{
"bodies": [
{
"name": "太阳",
"mass": 1.9891e+30,
"init_position": [
0.0,
0.0,
0.0
],
"init_velocity": [
0.0,
0.0,
0.0
],
"density": 1408.0,
"color": [
170,
98,
25
],
"texture": "sun2.jpg",
"size_scale": 50.0,
"distance_scale": 1.0,
"rotation_speed": 0.613,
"ignore_mass": false,
"is_fixed_star": true
}
],
"params": {
"dt": 604800,
"position": [
0,
149597870.7,
-448793612.09999996
]
}
}
\ No newline at end of file
{
"bodies": [
{
"name": "太阳",
"mass": 1.9891e+30,
"init_position": [
0.0,
0.0,
0.0
],
"init_velocity": [
0.0,
0.0,
0.0
],
"density": 1408.0,
"color": [
170,
98,
25
],
"texture": "sun2.jpg",
"size_scale": 50.0,
"distance_scale": 1.0,
"rotation_speed": 0.613,
"ignore_mass": false,
"is_fixed_star": true
},
{
"name": "地球",
"mass": 5.97237e+24,
"init_position": [
167549616.0,
0.0,
0.0
],
"init_velocity": [
0.0,
29.790000915527344,
0.0
],
"density": 5507.85,
"color": [
1,
89,
162
],
"texture": "earth1.jpg",
"size_scale": 2000.0,
"distance_scale": 1,
"rotation_speed": 15,
"ignore_mass": false,
"is_fixed_star": false
}
],
"params": {
"dt": 604800,
"position": [
0,
149597870.7,
-448793612.09999996
]
}
}
\ No newline at end of file
{
"bodies": [
{
"name": "太阳",
"mass": 1.9891e+30,
"init_position": [
149597872.0,
0.0,
0.0
],
"init_velocity": [
0.0,
0.0,
0.0
],
"density": 1408.0,
"color": [
170,
98,
25
],
"texture": "sun2.jpg",
"size_scale": 20.0,
"distance_scale": 1.0,
"rotation_speed": 0.613,
"ignore_mass": true,
"is_fixed_star": true
},
{
"name": "地球",
"mass": 5.97237e+24,
"init_position": [
0.0,
0.0,
0.0
],
"init_velocity": [
0.0,
0.0,
0.0
],
"density": 5507.85,
"color": [
1,
89,
162
],
"texture": "earth1.jpg",
"size_scale": 10.0,
"distance_scale": 1.0,
"rotation_speed": 15,
"ignore_mass": false,
"is_fixed_star": false
},
{
"name": "月球",
"mass": 7.342e+22,
"init_position": [
384400.0,
0.0,
0.0
],
"init_velocity": [
0.0,
1.0230000019073486,
0.0
],
"density": 3344.0,
"color": [
162,
162,
162
],
"texture": "moon.jpg",
"size_scale": 20.0,
"distance_scale": 1.0,
"rotation_speed": 0.5487,
"ignore_mass": false,
"is_fixed_star": false
}
],
"params": {
"dt": 86400,
"position": [
0,
0,
0
]
}
}
\ No newline at end of file
{
"bodies": [
{
"name": "红轨太阳A",
"mass": 2e+30,
"init_position": [
0.0,
518222240.0,
0.0
],
"init_velocity": [
-14.890000343322754,
0.0,
0.0
],
"density": 1408.0,
"color": [
255,
0,
0
],
"texture": "sun2.jpg",
"size_scale": 50.0,
"distance_scale": 1.0,
"is_fixed_star": true
},
{
"name": "绿轨太阳B",
"mass": 2e+30,
"init_position": [
-299195744.0,
0.0,
0.0
],
"init_velocity": [
7.445000171661377,
-12.895118713378906,
0.0
],
"density": 1408.0,
"color": [
0,
255,
0
],
"texture": "sun2.jpg",
"size_scale": 50.0,
"distance_scale": 1.0,
"is_fixed_star": true
},
{
"name": "蓝轨太阳C",
"mass": 2e+30,
"init_position": [
299195744.0,
0.0,
0.0
],
"init_velocity": [
7.445000171661377,
12.895118713378906,
0.0
],
"density": 1408.0,
"color": [
0,
0,
255
],
"texture": "sun2.jpg",
"size_scale": 50.0,
"distance_scale": 1.0,
"is_fixed_star": true
},
{
"name": "流浪地球",
"mass": 5.97237e+24,
"init_position": [
0.0,
86370368.0,
747989376.0
],
"init_velocity": [
0.0,
0.0,
-10.0
],
"density": 5507.85,
"color": [
1,
89,
162
],
"texture": "earth1.jpg",
"size_scale": 4000.0,
"distance_scale": 1
}
],
"params": {
"dt": 31536000,
"position": [
0,
149597870.7,
-1495978707.0
]
}
}
\ No newline at end of file
{
"bodies": [
{
"name": "红轨太阳A",
"mass": 2e+30,
"init_position": [
0.0,
518222240.0,
0.0
],
"init_velocity": [
-14.890000343322754,
0.0,
0.0
],
"density": 1408.0,
"color": [
255,
0,
0
],
"texture": "sun2.jpg",
"size_scale": 50.0,
"distance_scale": 1.0,
"is_fixed_star": true
},
{
"name": "绿轨太阳B",
"mass": 2e+30,
"init_position": [
-299195744.0,
0.0,
0.0
],
"init_velocity": [
7.445000171661377,
-12.895118713378906,
0.0
],
"density": 1408.0,
"color": [
0,
255,
0
],
"texture": "sun2.jpg",
"size_scale": 50.0,
"distance_scale": 1.0,
"is_fixed_star": true
},
{
"name": "蓝轨太阳C",
"mass": 2e+30,
"init_position": [
299195744.0,
0.0,
0.0
],
"init_velocity": [
7.445000171661377,
12.895118713378906,
0.0
],
"density": 1408.0,
"color": [
0,
0,
255
],
"texture": "sun2.jpg",
"size_scale": 50.0,
"distance_scale": 1.0,
"is_fixed_star": true
}
],
"params": {
"dt": 31536000,
"position": [
0,
149597870.7,
-1495978707.0
]
}
}
\ No newline at end of file
{
"bodies": [
{
"name": "红轨太阳A",
"mass": 2e+30,
"init_position": [
0.0,
"$exp:math.sqrt(3) * 2 * AU",
0.0
],
"init_velocity": [
-14.89,
0.0,
0.0
],
"density": 1408.0,
"color": [
255,
0,
0
],
"texture": "sun2.jpg",
"size_scale": 50.0,
"distance_scale": 1.0,
"is_fixed_star": true
},
{
"name": "绿轨太阳B",
"mass": 2e+30,
"init_position": [
"$exp: -2 * AU",
0.0,
0.0
],
"init_velocity": [
"$exp: 1/2 * 14.88",
"$exp:-math.sqrt(3) / 2 * 14.88",
0.0
],
"density": 1408.0,
"color": [
0,
255,
0
],
"texture": "sun2.jpg",
"size_scale": 50.0,
"distance_scale": 1.0,
"is_fixed_star": true
},
{
"name": "蓝轨太阳C",
"mass": 2e+30,
"init_position": [
"$exp: 2 * AU",
0.0,
0.0
],
"init_velocity": [
"$exp: 1/2 * 14.88",
"$exp:math.sqrt(3) / 2 * 14.88",
0.0
],
"density": 1408.0,
"color": [
0,
0,
255
],
"texture": "sun2.jpg",
"size_scale": 50.0,
"distance_scale": 1.0,
"is_fixed_star": true
}
],
"params": {
"dt": 31536000,
"position": [
0,
149597870.7,
-1495978707.0
]
}
}
\ No newline at end of file
# 模拟场景运行
## 文件说明
**func.py**
* 模拟场景用到的功能库
### 从运行demo开始
**demo.py**
* 演示入口,有详细的备注,大家可以在此基础上创建更多的模拟场景
### 模拟三体场景
**scenes/three_body_01.py**
* 3个太阳、1个地球(效果1)
**scenes/three_body_02.py**
* 3个太阳、1个地球(效果2)
### 模拟太阳系场景
**scenes/solar_system_1.py**
* 展示的效果为太阳系真实的距离,
由于宇宙空间尺度非常大,如果按照实际的天体大小,则无法看到天体,因此需要对天体的尺寸进行放大
**scenes/solar_system_2.py**
* 展示的效果非太阳系真实的距离和大小
1. 由于宇宙空间尺度非常大,如果按照实际的天体大小,则无法看到天体,因此需要对天体的尺寸进行放大
2. 为了达到最佳的显示效果,对每个行星天体的距离进行了缩放
**scenes/solar_system_3.py**
* 展示的效果非太阳系真实的距离和大小
1. 由于宇宙空间尺度非常大,按照实际的大小无法看到行星天体,因此需要对天体的尺寸进行放大
2. 为了达到最佳的显示效果,对每个行星天体的距离进行了缩放
3. 加入了小行星的演示效果**
**scenes/sun_earth.py**
* 太阳、地球运行效果
**scenes/sun_earth_jupiter.py**
* 太阳、地球、木星运行效果
# -*- coding:utf-8 -*-
# title :太阳、地球模拟运行
# description :太阳、地球模拟运行(演示案例)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Sun, Earth
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY
from scenes.func import mayavi_run, mpl_run, ursina_run
from bodies.body import Body, AU
if __name__ == '__main__':
"""
太阳、地球模拟运行
"""
# 构建两个天体对象(太阳、地球)
bodies = [
# 太阳的质量为 1.9891×10³⁰ kg
# 初始位置 x=0, y=0, z=0
# 初始速度 x=0, y=0, z=0
# 纹理图片路径为 texture/douyin.jpg
# 放大倍数为 120 倍
# 距离保持不变
Sun(name="抖音", mass=1.9891e30,
init_position=[0, 0, 0],
init_velocity=[0, 0, 0],
texture="douyin.jpg", size_scale=8e1, distance_scale=1.0),
# 地球的质量为 5.97237✕10²⁴ kg
# 初始位置 x=1.12天文单位, y=0, z=0
# 初始速度 x=0, y=29.7929.79 km/s, z=0
# 纹理图片路径为 texture/pythoncr.jpg
# 放大倍数为 5000 倍
# 距离保持不变
Earth(name="超人", mass=5.97237e24,
init_position=[2 * AU, 0, 0],
init_velocity=[0, 29.79, 0],
rotation_speed=1.5,
texture="pythoncr.jpg", size_scale=5e3, distance_scale=1.0),
]
# 使用 mayavi 查看的运行效果
# mayavi_run(bodies, SECONDS_PER_DAY, view_azimuth=135)
# 使用 matplotlib 查看运行效果
# mpl_run(bodies, SECONDS_PER_WEEK)
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_WEEK, position=(0, AU, -5 * AU))
# -*- coding:utf-8 -*-
# title :地球晚上模拟运行
# description :地球晚上模拟运行
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Earth
from common.consts import SECONDS_PER_HOUR
from scenes.func import ursina_run
if __name__ == '__main__':
"""
高清水星模拟运行
"""
bodies = [
Earth(texture="earth_at_night.jpg",
init_position=[0, 0, 0], init_velocity=[0, 0, 0],
size_scale=100.0001, ignore_mass=True)
]
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_HOUR / 2, position=(0, 200000, -2000000), cosmic_bg="../textures/cosmic2.jpg")
# -*- coding:utf-8 -*-
# title :地月场景模拟
# description :地月场景模拟
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Sun, Earth, Moon
from common.consts import SECONDS_PER_HOUR, SECONDS_PER_HALF_DAY, SECONDS_PER_DAY, SECONDS_PER_WEEK, SECONDS_PER_MONTH
from scenes.func import mayavi_run, ursina_run
from bodies.body import AU
if __name__ == '__main__':
"""
地球、月球
"""
# 地球的Y方向初始速度
EARTH_INIT_VELOCITY = 0
bodies = [
# sun,
Earth(init_position=[0, 0, 0],
init_velocity=[0, EARTH_INIT_VELOCITY, 0], size_scale=0.5e1), # 地球放大 5 倍,距离保持不变
Moon(init_position=[363104, 0, 0], # 距地距离约: 363104 至 405696 km
init_velocity=[0, EARTH_INIT_VELOCITY + 1.023, 0], size_scale=1e1) # 月球放大 10 倍,距离保持不变
]
# mayavi_run(bodies, SECONDS_PER_HALF_DAY / 2, view_azimuth=-45)
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_MONTH, position=(-300000, 300000, -1000000), show_trail=True)
# -*- coding:utf-8 -*-
# title :恒星演示
# description :恒星演示
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Sun, Earth, Sirius, Rigel, Bellatrix, Alcyone, Antares, Arcturus, Aldebaran, Betelgeuse
from bodies import EtaCarinae, YCanumVenaticorum, VYCanisMajoris, UYScuti, CarinaeV382, Stephenson_2_18
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_MONTH, SECONDS_PER_YEAR, SECONDS_PER_DAY
from scenes.func import mayavi_run, mpl_run, ursina_run
from bodies.body import Body, AU
if __name__ == '__main__':
"""
恒星演示
"""
# 构建两个天体对象(太阳、地球)
D = 5e5
SIZE_SCALE = 0.5
bodies = [
Earth(size_scale=SIZE_SCALE, ignore_mass=True),
Sun(size_scale=SIZE_SCALE, ignore_mass=True), # 太阳
Sirius(size_scale=SIZE_SCALE, ignore_mass=True), # 天狼星A 质量倍数 2.02 半径倍数 1.71
# Bellatrix(size_scale=SIZE_SCALE, ignore_mass=True), # 参宿五 质量倍数 8.6 半径倍数 5.75
Alcyone(size_scale=SIZE_SCALE, ignore_mass=True), # 昴宿六 质量倍数 6 半径倍数 9.5
Arcturus(size_scale=SIZE_SCALE, ignore_mass=True), # 大角星 质量倍数 1.1 半径倍数 25.7
Aldebaran(size_scale=SIZE_SCALE, ignore_mass=True), # 毕宿五 质量倍数 1.16 半径倍数 44.13
Rigel(size_scale=SIZE_SCALE, ignore_mass=True), # 参宿七 质量倍数 18 半径倍数 78
# YCanumVenaticorum(size_scale=SIZE_SCALE, ignore_mass=True), # 猎犬座Y 质量倍数 3.0 半径倍数 215
EtaCarinae(size_scale=SIZE_SCALE, ignore_mass=True), # 海山二 质量倍数 125 半径倍数 278
# Antares(size_scale=SIZE_SCALE, ignore_mass=True), # 心宿二 质量倍数 15 半径倍数 680
CarinaeV382(size_scale=SIZE_SCALE, ignore_mass=True), # 船底座V382 质量倍数 39 半径倍数 747
# Betelgeuse(size_scale=SIZE_SCALE, ignore_mass=True), # 参宿四 质量倍数 19 半径倍数 1180
VYCanisMajoris(size_scale=SIZE_SCALE, ignore_mass=True), # 大犬座VY 质量倍数 30 半径倍数 1400
# UYScuti(size_scale=SIZE_SCALE, ignore_mass=True), # 盾牌座 UY 质量倍数 10 半径倍数 1708
Stephenson_2_18(size_scale=SIZE_SCALE, ignore_mass=True) # 史蒂文森2-18 质量倍数 40.0 半径倍数 2150
]
distance_sum = 0
for idx, body in enumerate(bodies):
body.rotation_speed /= 10
if body.is_fixed_star:
body.light_on = False # 关闭灯光效果,只有太阳对地球有灯光效果
if idx == 0:
d = 0
else:
d = pow((body.raduis + bodies[idx - 1].raduis) * SIZE_SCALE, 1.0) * 1.1
# d = (body.diameter + bodies[idx - 1].diameter) * SIZE_SCALE * 1.1 + D
body.init_velocity = [0, 0, 0]
body.init_position = [body.raduis * SIZE_SCALE, (distance_sum + d), AU]
distance_sum += d
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_WEEK, position=(0, AU, -AU / 500),
show_name=True, bg_music="../sounds/universe_03.mp3")
# -*- coding:utf-8 -*-
# title :场景用功能库
# description :场景用功能库
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
import matplotlib.pyplot as plt
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY, SECONDS_PER_HALF_DAY
from common.system import System
from simulators.ursina.ursina_config import UrsinaConfig
from simulators.ursina.ursina_event import UrsinaEvent
def mayavi_run(bodies, dt=SECONDS_PER_WEEK,
view_azimuth=0, view_distance='auto', view_focalpoint='auto',
bgcolor=(1 / 255, 1 / 255, 30 / 255)):
"""
用 mayavi 查看运行效果
:param bodies: 天体
:param dt: 单位:秒,按时间差进行演变,值越小越精确,但演变速度会慢。
:param view_azimuth: 观测方位角,可选,float类型(以度为单位,0-360),用x轴投影到x-y平面上的球体上的位置矢量所对的角度。
:param view_distance: 观测距离,可选,float类型 or 'auto',一个正浮点数,表示距放置相机的焦点的距离。
:param view_focalpoint: 观测焦点,可选,类型为一个由3个浮点数组成的数组 or 'auto',,代表观测相机的焦点
:param bgcolor:
:return:
"""
from mayavi import mlab
from simulators.mayavi_simulator import MayaviSimulator
# 宇宙背景色
mlab.figure(bgcolor=bgcolor, size=(1440, 810))
body_sys = System(bodies)
simulator = MayaviSimulator(body_sys)
simulator.run(dt)
# azimuth:
# 观测方位角,可选,float类型(以度为单位,0-360),用x轴投影到x-y平面上的球体上的位置矢量所对的角度。
# elevation:
# 观测天顶角,可选,float类型(以度为单位,0-180), 位置向量和z轴所对的角度。
# distance:
# 观测距离,可选,float类型 or 'auto',一个正浮点数,表示距放置相机的焦点的距离。
# Mayavi 3.4.0中的新功能:'auto' 使得距离为观察所有对象的最佳位置。
# focalpoint:
# 观测焦点,可选,类型为一个由3个浮点数组成的数组 or 'auto',,代表观测相机的焦点
# Mayavi 3.4.0中的新功能:'auto',则焦点位于场景中所有对象的中心。
# roll:
# 控制滚动,可选,float类型,即摄影机围绕其轴的旋转
# reset_roll:
# 布尔值,可选。如果为True,且未指定“滚动”,则重置相机的滚动方向。
# figure:
# 要操作的Mayavi图形。如果为 None,则使用当前图形。
mlab.view(azimuth=view_azimuth, distance=view_distance, focalpoint=view_focalpoint)
# mlab.view(azimuth=-45, elevation=45, distance=100e8 * 2 * 2 * 4 * 4, focalpoint=[5e10, 5e10, 5e9])
mlab.show()
def ursina_run(bodies,
dt=SECONDS_PER_HALF_DAY,
position=(0, 0, 0),
# view_azimuth=0,
light=True,
cosmic_bg=None,
bg_music=None,
show_grid=True,
show_trail=False,
show_name=False,
save_as_json=None):
"""
:param bodies: 天体
:param dt: 单位:秒,按时间差进行演变,值越小越精确,但演变速度会慢。
:param position: 摄像头位置
:param view_azimuth: 摄像头观测方位角,可选,float类型(以度为单位,0-360)
:param light: 使用灯光效果
:param cosmic_bg: 宇宙背景图片
:param show_grid: 是否显示空间网格
:param save_as_json: 将所有天体的信息保存为 json 文件
:param ignore_mass: 忽略所有天体的引力
:return:
"""
from simulators.ursina_simulator import UrsinaSimulator, UrsinaPlayer
from ursina import application, Sequence, camera, held_keys, time, clamp, Entity, Text, color
from ursina.prefabs.first_person_controller import FirstPersonController
body_sys = System(bodies)
if show_name:
for body in body_sys.bodies:
body.show_name = True
if save_as_json is not None:
try:
body_sys.save_to_json(save_as_json, {"dt": dt, "position": position,
"show_trail": show_trail, "show_name": show_name})
print(f"{save_as_json} 文件生成成功!")
except Exception as e:
print(f"{save_as_json} 文件生成失败!" + str(e))
return
simulator = UrsinaSimulator(body_sys)
view_azimuth = 0 # 暂时未用
player = UrsinaPlayer(position, view_azimuth, simulator.ursina_views)
# # player = FirstPersonController(model='cube', y=-1e20, color=color.orange, origin_y=-5000, speed=8)
# # player.on_disable()
# # player.position = position
#
# player = FirstPersonController()
# cube = Entity(model='cube', color=color.red, scale=2)
# player.parent = cube # 设置 FirstPersonController 的父实体为 cube
# cube.position = position # 修改父实体的位置,从而间接地修改 FirstPersonController 的位置
# # 创建一个实体(在屏幕中央)和一个摄像机
# TODO: 未使用
# entity = Entity(model='cube', position=(0, 0, 5), scale=2)
# camera = Camera()
#
# # 设置初始的 FOV 值(默认值为 90)
# camera.fov = 60
#
# # 创建一个用于显示当前 FOV 值的文本
# fov_text = Text(text=f'FOV: {camera.fov}', position=(-0.5, 0.4), scale=2)
# # 每一帧更新摄像机 FOV 值
# def update():
# # 通过鼠标滚轮来调整 FOV 值
# camera.fov -= held_keys['scroll'] * 10 * time.dt
# # 限制 FOV 值的范围(1 到 120 之间)
# camera.fov = clamp(camera.fov, 1, 120)
# # 更新文本内容
# fov_text.text = f'FOV: {camera.fov:.2f}' # 保留两位小数
#
# # 将摄像机移到实体旁边,并对着它
# camera.position = entity.position + (0, 0, -5)
# camera.look_at(entity.position)
def callback_update():
UrsinaEvent.on_application_run()
for ursina_view in simulator.ursina_views:
simulator.check_and_evolve()
if ursina_view.appeared:
ursina_view.update()
# print('....')
import sys
sys.modules["__main__"].update = callback_update
if show_trail:
UrsinaConfig.show_trail = show_trail
simulator.run(dt, light=light, cosmic_bg=cosmic_bg, show_grid=show_grid, bg_music=bg_music)
def mpl_run(bodies, dt=SECONDS_PER_WEEK, gif_file_name=None, gif_max_frame=200):
"""
:param bodies: 天体
:param dt: 单位:秒,按时间差进行演变,值越小越精确,但演变速度会慢。
:param gif_file_name: 导出的 gif 文件名,如果为空,则显示动画
:return:
"""
from simulators.mpl_simulator import MplSimulator
body_sys = System(bodies)
simulator = MplSimulator(body_sys)
simulator.run(dt, gif_file_name=gif_file_name, gif_max_frame=gif_max_frame)
COSMIC_BG_COLOR = "#002563"
COSMIC_FORE_COLOR = "white"
def create_fig_ax(styles={}):
bg_color = styles["bg_color"] if "bg_color" in styles else COSMIC_BG_COLOR
fore_color = styles["fore_color"] if "fore_color" in styles else COSMIC_FORE_COLOR
if bg_color is None:
fig = plt.figure('天体模拟运行效果', figsize=(20, 12))
else:
fig = plt.figure('天体模拟运行效果', figsize=(20, 12), facecolor=bg_color)
ax = fig.gca(projection="3d")
return fig, ax
if __name__ == '__main__':
from bodies import Sun, Earth
"""
太阳、地球
"""
bodies = [
Sun(size_scale=1.2e2), # 太阳放大 120 倍
Earth(size_scale=4e3, distance_scale=1), # 地球放大 4000 倍,距离保持不变
]
# mpl_run(bodies, SECONDS_PER_WEEK)
ursina_run(bodies, SECONDS_PER_WEEK)
# -*- coding:utf-8 -*-
# title :引力弹弓模拟演示
# description :引力弹弓模拟演示
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Sun, Earth, Moon
from common.consts import SECONDS_PER_HOUR, SECONDS_PER_HALF_DAY, SECONDS_PER_DAY, SECONDS_PER_WEEK, SECONDS_PER_MONTH
from scenes.func import mayavi_run, ursina_run
from bodies.body import AU
if __name__ == '__main__':
"""
太阳、地球
"""
bodies = [
Sun(size_scale=2e1), # 太阳放大 20 倍
Earth(size_scale=1e3, # 地球放大 1000 倍
init_position=[0, -3 * AU, 0], # 地球距离太阳 3 个天文单位
# TODO: 尝试调整朝向太阳的速度,取值 33、38、50 或者其他
# init_velocity=[0, 33, -1],
init_velocity=[0, 38, -1], # 朝向太阳的速度为 38km/s,-1 km/s 是为了防止地球正面对着太阳冲去
# init_velocity=[0, 50, -1],
),
]
# 使用 mayavi 查看的运行效果
# mayavi_run(bodies, SECONDS_PER_WEEK, view_azimuth=-45)
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_MONTH, position=(0, AU, -3 * AU), show_trail=True)
# -*- coding:utf-8 -*-
# title :高清水星模拟运行
# description :高清水星模拟运行
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Mercury
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY
from scenes.func import ursina_run
if __name__ == '__main__':
"""
高清水星模拟运行
"""
bodies = [
Mercury(texture="mercury_hd.tif",
init_position=[0, 0, 0], init_velocity=[0, 0, 0],
size_scale=100)
]
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_DAY, position=(0, 30000, -600000), cosmic_bg="../textures/cosmic1.jpg")
# -*- coding:utf-8 -*-
# title :太阳系场景模拟1
# description :太阳系场景模拟(展示的效果为太阳系真实的距离)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Sun, Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Moon, Asteroids
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY, SECONDS_PER_YEAR, AU
from scenes.func import mayavi_run, ursina_run
if __name__ == '__main__':
# 八大行星:木星(♃)、土星(♄)、天王星(♅)、海王星(♆)、地球(⊕)、金星(♀)、火星(♂)、水星(☿)
# 排列顺序
# 1、体积:(以地球为1)木星 :土星 :天王星 :海王星 :地球 :金星 :火星 :水星 = 1330:745:65:60:1:0.86:0.15:0.056
# 2、质量:(以地球为1)木星 :土星 :天王星 :海王星 :地球 :金星 :火星 :水星 = 318:95:14.53:17.15:1:0.8:0.11:0.0553
# 3、离太阳从近到远的顺序:水星、金星、地球、火星、木星、土星、天王星、海王星
# =====================================================================
# 以下展示的效果为太阳系真实的距离
# 由于宇宙空间尺度非常大,如果按照实际的天体大小,则无法看到天体,因此需要对天体的尺寸进行放大
sun = Sun(name="太阳", size_scale=0.8e2) # 太阳放大 80 倍,距离保持不变
# sun.init_velocity = [0, 0, 2] # 太阳带着其他行星一起跑
bodies = [
sun,
Mercury(name="水星", size_scale=4e3), # 水星放大 4000 倍,距离保持不变
Venus(name="金星", size_scale=4e3), # 金星放大 4000 倍,距离保持不变
Earth(name="地球", size_scale=4e3), # 地球放大 4000 倍,距离保持不变
Mars(name="火星", size_scale=4e3), # 火星放大 4000 倍,距离保持不变
Asteroids(name="小行星群", size_scale=3.2e2,
parent=sun), # 小行星群模拟(仅 ursina 模拟器支持)
Jupiter(name="木星", size_scale=0.8e3), # 木星放大 800 倍,距离保持不变
Saturn(name="土星", size_scale=0.8e3), # 土星放大 800 倍,距离保持不变
Uranus(name="天王星", size_scale=0.8e3), # 天王星放大 800 倍,距离保持不变
Neptune(name="海王星", size_scale=1e3), # 海王星放大 1000 倍,距离保持不变
Pluto(name="冥王星", size_scale=10e3), # 冥王星放大 10000 倍,距离保持不变(从太阳系的行星中排除)
]
# 使用 mayavi 查看的运行效果
# mayavi_run(bodies, SECONDS_PER_WEEK, view_azimuth=-45)
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_YEAR, position=(0, 2 * AU, -11 * AU), bg_music="../sounds/universe_04.mp3")
# -*- coding:utf-8 -*-
# title :太阳系场景模拟2
# description :太阳系场景模拟(展示的效果非太阳系真实的距离和大小)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Sun, Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Asteroids
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY, SECONDS_PER_MONTH, SECONDS_PER_YEAR, AU
from scenes.func import mayavi_run, ursina_run
if __name__ == '__main__':
# 八大行星:木星(♃)、土星(♄)、天王星(♅)、海王星(♆)、地球(⊕)、金星(♀)、火星(♂)、水星(☿)
# 排列顺序
# 1、体积:(以地球为1)木星 :土星 :天王星 :海王星 :地球 :金星 :火星 :水星 = 1330:745:65:60:1:0.86:0.15:0.056
# 2、质量:(以地球为1)木星 :土星 :天王星 :海王星 :地球 :金星 :火星 :水星 = 318:95:14.53:17.15:1:0.8:0.11:0.0553
# 3、离太阳从近到远的顺序:水星、金星、地球、火星、木星、土星、天王星、海王星
# =====================================================================
# 以下展示的效果非太阳系真实的距离和大小
# 1、由于宇宙空间尺度非常大,如果按照实际的天体大小,则无法看到天体,因此需要对天体的尺寸进行放大
# 2、为了达到最佳的显示效果,对每个行星天体的距离进行了缩放
# region 构建太阳系
sun = Sun(size_scale=0.8e2)
bodies = [
sun, # 太阳放大 80 倍
Mercury(size_scale=4e3, distance_scale=1.3), # 水星放大 4000 倍,距离放大 1.3 倍
Venus(size_scale=4e3, distance_scale=1.3), # 金星放大 4000 倍,距离放大 1.3 倍
Earth(size_scale=4e3, distance_scale=1.3), # 地球放大 4000 倍,距离放大 1.3 倍
Asteroids(size_scale=3e2, parent=sun), # 小行星模拟(仅 ursina 模拟器支持)
Mars(size_scale=4e3, distance_scale=1.3), # 火星放大 4000 倍,距离放大 1.3 倍
Jupiter(size_scale=0.68e3, distance_scale=0.65), # 木星放大 680 倍,距离缩小到真实距离的 0.65
Saturn(size_scale=0.68e3, distance_scale=0.52), # 土星放大 680 倍,距离缩小到真实距离的 0.52
Uranus(size_scale=0.8e3, distance_scale=0.36), # 天王星放大 800 倍,距离缩小到真实距离的 0.36
Neptune(size_scale=1e3, distance_scale=0.27), # 海王星放大 1000 倍,距离缩小到真实距离的 0.27
Pluto(size_scale=10e3, distance_scale=0.23), # 冥王星放大 10000 倍,距离缩小到真实距离的 0.23(从太阳系的行星中排除)
]
# endregion
# 使用 mayavi 查看的运行效果
# mayavi_run(bodies, SECONDS_PER_WEEK, view_azimuth=-45, view_distance=3e9, view_focalpoint=[5e2, 5e2, 5e2])
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_YEAR, position=(0, 2 * AU, -11 * AU))
# -*- coding:utf-8 -*-
# title :太阳系场景模拟3
# description :太阳系场景模拟(展示的效果非太阳系真实的距离和大小,加入了小行星的演示效果)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Sun, Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Asteroid
from bodies.body import AU
from common.consts import SECONDS_PER_WEEK
from common.func import get_positions_velocitys
from scenes.func import mayavi_run
import numpy as np
if __name__ == '__main__':
# 八大行星:木星(♃)、土星(♄)、天王星(♅)、海王星(♆)、地球(⊕)、金星(♀)、火星(♂)、水星(☿)
# 排列顺序
# 1、体积:(以地球为1)木星 :土星 :天王星 :海王星 :地球 :金星 :火星 :水星 = 1330:745:65:60:1:0.86:0.15:0.056
# 2、质量:(以地球为1)木星 :土星 :天王星 :海王星 :地球 :金星 :火星 :水星 = 318:95:14.53:17.15:1:0.8:0.11:0.0553
# 3、离太阳从近到远的顺序:水星、金星、地球、火星、木星、土星、天王星、海王星
# =====================================================================
# 以下展示的效果非太阳系真实的距离和大小
# 1、由于宇宙空间尺度非常大,按照实际的大小无法看到行星天体,因此需要对天体的尺寸进行放大
# 2、为了达到最佳的显示效果,对每个行星天体的距离进行了缩放
# 3、加入了小行星的演示效果
# region 1.构建4个小行星 -------------
# asteroids = [
# Asteroid(size_scale=3e9, # 小行星放大 1000000000 倍,距离保持不变
# init_position=[1.6 * AU, 0, 0],
# init_velocity=[0, 25.37, 0],
# distance_scale=1),
# Asteroid(size_scale=3e9, # 小行星放大 1000000000 倍,距离保持不变
# init_position=[-1.6 * AU, 0, 0],
# init_velocity=[0, -25.37, 0],
# distance_scale=1),
# Asteroid(size_scale=3e9, # 小行星放大 1000000000 倍,距离保持不变
# init_position=[0, 1.6 * AU, 0],
# init_velocity=[-25.37, 0, 0],
# distance_scale=1),
# Asteroid(size_scale=3e9, # 小行星放大 1000000000 倍,距离保持不变
# init_position=[0, -1.6 * AU, 0],
# init_velocity=[25.37, 0, 0],
# distance_scale=1),
# ]
# endregion 1 --------------------------
# region 2.随机构建 60 小行星,注意:太多的小行星会影响电脑性能
NUM_OF_ASTEROIDS = 60
asteroids = []
angles = np.linspace(0, 40 * np.pi, NUM_OF_ASTEROIDS)
pxs, pys, vxs, vys = get_positions_velocitys(angles,
radius=1.60 * AU,
velocity=23.37,
radius_offset=0.1 * AU,
velocity_offset=0.2)
for i, px in enumerate(pxs):
py, fx, fy = pys[i], vxs[i], vys[i]
asteroids.append(Asteroid(size_scale=3e9, # 小行星放大 5000000000 倍,距离放大 1.4 倍
init_position=[px, py, 0],
init_velocity=[fx, fy, 0],
distance_scale=1.4))
# endregion 2
# region 3.构建太阳系
bodies = [
Sun(size_scale=0.8e2), # 太阳放大 80 倍
Mercury(size_scale=4e3, distance_scale=1.3), # 水星放大 4000 倍,距离放大 1.3 倍
Venus(size_scale=4e3, distance_scale=1.3), # 金星放大 4000 倍,距离放大 1.3 倍
Earth(size_scale=4e3, distance_scale=1.3), # 地球放大 4000 倍,距离放大 1.3 倍
Mars(size_scale=4e3, distance_scale=1.3), # 火星放大 4000 倍,距离放大 1.3 倍
Jupiter(size_scale=0.68e3, distance_scale=0.65), # 木星放大 680 倍,距离缩小到真实距离的 0.65
Saturn(size_scale=0.68e3, distance_scale=0.52), # 土星放大 680 倍,距离缩小到真实距离的 0.52
Uranus(size_scale=0.8e3, distance_scale=0.36), # 天王星放大 800 倍,距离缩小到真实距离的 0.36
Neptune(size_scale=1e3, distance_scale=0.27), # 海王星放大 1000 倍,距离缩小到真实距离的 0.27
Pluto(size_scale=10e3, distance_scale=0.23), # 冥王星放大 10000 倍,距离缩小到真实距离的 0.23(从太阳系的行星中排除)
]
# 增加小行星到太阳系
bodies += asteroids
# endregion 3
# 使用 mayavi 查看的运行效果
# mayavi_run(bodies, SECONDS_PER_WEEK, view_azimuth=-45)
mayavi_run(bodies, SECONDS_PER_WEEK, view_azimuth=-45, view_distance=3e9, view_focalpoint=[5e2, 5e2, 5e2])
# -*- 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 / 4, -2 * AU), bg_music="../sounds/universe_02.mp3")
# -*- coding:utf-8 -*-
# title :太阳、地球场景模拟
# description :太阳、地球场景模拟
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Sun, Earth, Sirius
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY, AU
from scenes.func import mayavi_run, ursina_run
if __name__ == '__main__':
"""
太阳、地球 Sirius(size_scale=0.2, init_position=[0, 0, 0]),
"""
bodies = [
Sun(size_scale=5e1), # 太阳放大 50 倍
Earth(size_scale=2e3, distance_scale=1), # 地球放大 2000 倍,距离保持不变
]
# 使用 mayavi 查看的运行效果
# mayavi_run(bodies, SECONDS_PER_WEEK, view_azimuth=-45)
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_WEEK, position=(0, AU, -3 * AU), show_trail=True)
# -*- coding:utf-8 -*-
# title :太阳、地球、木星场景模拟
# description :太阳、地球、木星场景模拟
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Sun, Earth, Jupiter
from common.consts import SECONDS_PER_WEEK, AU
from scenes.func import mayavi_run, ursina_run
if __name__ == '__main__':
"""
太阳、地球、木星
"""
bodies = [
Sun(size_scale=5e1), # 太阳放大 50 倍
Earth(size_scale=2e3, distance_scale=1), # 地球放大 2000 倍,距离保持不变
Jupiter(size_scale=5e2, distance_scale=1), # 木星放大 500 倍,距离保持不变
]
# 使用 mayavi 查看的运行效果
# mayavi_run(bodies, SECONDS_PER_WEEK, view_azimuth=-45)
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_WEEK, position=(0, AU, -3 * AU), show_trail=True)
# -*- coding:utf-8 -*-
# title :地月场景模拟(观看月相变化的过程)
# description :地月场景模拟(观看月相变化的过程)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Sun, Earth, Moon
from common.consts import SECONDS_PER_HOUR, SECONDS_PER_HALF_DAY, SECONDS_PER_DAY, SECONDS_PER_WEEK, SECONDS_PER_MONTH
from scenes.func import mayavi_run, ursina_run
from bodies.body import AU
if __name__ == '__main__':
"""
地球、月球
"""
# 地球的Y方向初始速度
EARTH_INIT_VELOCITY = 0 # 0km/s
sun = Sun(init_position=[AU, 0, 0], size_scale=2e1) # 太阳放大 20 倍
# 忽略质量的引力
sun.ignore_mass = True
# 观看月相变化的过程:分别是 新月、蛾眉月、上弦月、盈凸、满月、亏凸、下弦月、残月
# 参考:images/moon/月相变化过程.jpeg
# TODO: 月球在摄像机的前方(从 “新月” 开始)
moon_pos, moon_vel = [384400, 0, 0], [0, EARTH_INIT_VELOCITY + 1.023, 0]
# TODO: 月球在摄像机的右方(从 “下弦月” 开始),将会出现
# moon_pos, moon_vel = [0, -384400, 0], [1.023, EARTH_INIT_VELOCITY, 0]
# TODO: 月月球在摄像机的左方(从 “上弦月” 开始)
# moon_pos, moon_vel = [0, 384400, 0], [-1.023, EARTH_INIT_VELOCITY, 0]
bodies = [
sun,
Earth(init_position=[0, 0, 0],
init_velocity=[0, EARTH_INIT_VELOCITY, 0],
size_scale=1e1), # 地球放大 10 倍,距离保持不变
Moon(init_position=moon_pos, # 距地距离约: 363104 至 405696 km
init_velocity=moon_vel,
size_scale=2e1) # 月球放大 20 倍,距离保持不变
]
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
# position=(0, 0, 0) 的位置是站在地球视角,可以观看月相变化的过程
ursina_run(bodies, SECONDS_PER_DAY, position=(0, 0, 0))
# 使用 mayavi 查看的运行效果
# mayavi_run(bodies, SECONDS_PER_HALF_DAY / 2, view_azimuth=-45)
# -*- coding:utf-8 -*-
# title :三体场景模拟01
# description :三体场景模拟(3个太阳、1个地球)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Sun, Earth
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_YEAR, SECONDS_PER_MONTH, AU
from scenes.func import mayavi_run, ursina_run
if __name__ == '__main__':
"""
3个太阳、1个地球(效果1)
可以修改影响效果的参数为:
1、三个方向的初始位置 init_position[x, y, z]
2、三个方向的初始速度 init_velocity[x, y, z]
3、天体质量 mass
"""
bodies = [
Sun(mass=1.5e30, init_position=[849597870.700, 0, 0], init_velocity=[0, 7.0, 0],
size_scale=5e1, texture="sun2.jpg"), # 太阳放大 100 倍
Sun(mass=2e30, init_position=[0, 0, 0], init_velocity=[0, -8.0, 0],
size_scale=5e1, texture="sun2.jpg"), # 太阳放大 100 倍
Sun(mass=2.5e30, init_position=[0, -849597870.700, 0], init_velocity=[18.0, 0, 0],
size_scale=5e1, texture="sun2.jpg"), # 太阳放大 100 倍
Earth(init_position=[0, -349597870.700, 0], init_velocity=[15.50, 0, 0],
size_scale=4e3, distance_scale=1), # 地球放大 4000 倍,距离保持不变
]
# 使用 mayavi 查看的运行效果
# mayavi_run(bodies, SECONDS_PER_WEEK, view_azimuth=0)
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_YEAR, position=(3 * AU, AU, -4 * AU), show_trail=True)
\ No newline at end of file
# -*- coding:utf-8 -*-
# title :三体场景模拟02
# description :三体场景模拟(3个太阳、1个地球)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Sun, Earth
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY
from scenes.func import mayavi_run, ursina_run
if __name__ == '__main__':
"""
3个太阳、1个地球(效果2)
可以修改影响效果的参数为:
1、三个方向的初始位置 init_position[x, y, z]
2、三个方向的初始速度 init_velocity[x, y, z]
3、天体质量 mass
"""
bodies = [
Sun(mass=5e30, init_position=[649597870.700, 0, 0], init_velocity=[0, 5.0, 0],
size_scale=5e1, texture="sun1.jpg"), # 太阳放大 100 倍
Sun(mass=4e30, init_position=[0, 0, 249597870.700], init_velocity=[0, -6.0, 0],
size_scale=5e1, texture="sun2.jpg"), # 太阳放大 100 倍
Sun(mass=3e30, init_position=[0, -649597870.700, 0], init_velocity=[6.0, 0, 0],
size_scale=5e1, texture="sun2.jpg"), # 太阳放大 100 倍
Earth(init_position=[0, -249597870.700, 0], init_velocity=[15.50, 0, 0],
size_scale=4e3, distance_scale=1), # 地球放大 4000 倍,距离保持不变
]
# 使用 mayavi 查看的运行效果
mayavi_run(bodies, SECONDS_PER_WEEK, view_azimuth=0)
# -*- coding:utf-8 -*-
# title :三体场景模拟02
# description :三体场景模拟(3个太阳、1个地球)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Sun, Earth
from common.consts import SECONDS_PER_DAY, SECONDS_PER_WEEK, SECONDS_PER_YEAR, AU
from scenes.func import mayavi_run, mpl_run, ursina_run
if __name__ == '__main__':
"""
注释: 3个太阳(ChatGPT生成的效果)
可以修改影响效果的参数为:
1、三个方向的初始位置 init_position[x, y, z]
2、三个方向的初始速度 init_velocity[x, y, z]
3、天体质量 mass
"""
# 代码案例如下:
SIZE_SCALE = 5e1 # 生成的太阳放大 50 倍
RUN_DIAMETER = 1.392e6 # 真实太阳的直径
bodies = [
Sun(name="太阳1", mass=2.5e30, init_position=[100 * RUN_DIAMETER, 200 * RUN_DIAMETER, 300 * RUN_DIAMETER],
init_velocity=[-12.5, -12.0, 11.5],
size_scale=SIZE_SCALE, texture="sun1.jpg"),
Sun(name="太阳2", mass=2e30, init_position=[-150 * RUN_DIAMETER, 250 * RUN_DIAMETER, 350 * RUN_DIAMETER],
init_velocity=[11.5, 12.0, 12.5],
size_scale=SIZE_SCALE, texture="sun2.jpg"),
Sun(name="太阳3", mass=2.8e30, init_position=[200 * RUN_DIAMETER, -300 * RUN_DIAMETER, 400 * RUN_DIAMETER],
init_velocity=[-11.5, -12.5, -12.0],
size_scale=SIZE_SCALE, texture="sun2.jpg"),
]
# 按照以上代码案例格式生成代码,要求 init_position 、init_velocity、mass 生成后,要保证在万有引力的作用下,能正常运行,不会碰撞
"""
太阳1:
初始位置:(100000, 200000, 300000) km
初始速度:(-1.0, -2.0, 3.0) km/s
质量:2.5 x 10^30 kg
太阳2:
初始位置:(-150000, 250000, 350000) km
初始速度:(2.0, -3.0, -1.0) km/s
质量:2.0 x 10^30 kg
太阳3:
初始位置:(200000, -300000, 400000) km
初始速度:(-3.0, 1.0, -2.0) km/s
质量:2.8 x 10^30 kg
"""
# 使用 mayavi 查看的运行效果
# mayavi_run(bodies, SECONDS_PER_WEEK, view_azimuth=0)
# 使用 matplotlib 查看运行效果
# mpl_run(bodies, SECONDS_PER_WEEK)
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_YEAR, position=(3 * AU, 3 * AU, -20 * AU), show_trail=True)
# -*- coding:utf-8 -*-
# title :三体场景模拟01
# description :三体场景模拟(3个太阳、1个地球)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Sun, Earth, FixedStar
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY, AU, SECONDS_PER_MONTH, SECONDS_PER_YEAR
from scenes.func import mayavi_run, ursina_run
if __name__ == '__main__':
"""
3个太阳、1个地球(效果1)
可以修改影响效果的参数为:
1、三个方向的初始位置 init_position[x, y, z]
2、三个方向的初始速度 init_velocity[x, y, z]
3、天体质量 mass
"""
import math
mass = 2e30
r = 2 * AU
# p = 12 # TODO: 三体转圆形花
p = 14.88 # TODO: 三体转圈近似圆形
# p = 16 # TODO: 三体转圆形花
# p = 18 # TODO: 三体转圆形花
# p = 19 # TODO: 三体转圆形花
bodies = [
Sun(name="红轨太阳A", mass=mass,
init_position=[0, math.sqrt(3) * r, 0],
init_velocity=[-p, 0, 0],
trail_color=(255, 0, 0),
size_scale=5e1, texture="sun2.jpg"), # 太阳放大 100 倍
Sun(name="绿轨太阳B", mass=mass,
init_position=[-r, 0, 0],
init_velocity=[1 / 2 * p, -math.sqrt(3) / 2 * p, 0],
trail_color=(0, 255, 0),
size_scale=5e1, texture="sun2.jpg"), # 太阳放大 100 倍
Sun(name="蓝轨太阳C", mass=mass,
init_position=[r, 0, 0],
init_velocity=[1 / 2 * p, math.sqrt(3) / 2 * p, 0],
trail_color=(0, 0, 255),
size_scale=5e1, texture="sun2.jpg"), # 太阳放大 100 倍
# Earth(init_position=[0, -349597870.700, 0],
# init_velocity=[15.50, 0, 0],
# size_scale=4e3, distance_scale=1), # 地球放大 4000 倍,距离保持不变
]
# 使用 mayavi 查看的运行效果
# mayavi_run(bodies, SECONDS_PER_WEEK, view_azimuth=0)
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_YEAR, position=(-2 * AU, AU, -5 * AU), show_trail=True)
# -*- coding:utf-8 -*-
# title :三体场景模拟01
# description :三体场景模拟(2个太阳、1个地球)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Sun, Earth
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_YEAR, AU
from scenes.func import mayavi_run, mpl_run, ursina_run
if __name__ == '__main__':
"""
2个太阳、1个地球(效果1)
可以修改影响效果的参数为:
1、三个方向的初始位置 init_position[x, y, z]
2、三个方向的初始速度 init_velocity[x, y, z]
3、天体质量 mass
"""
bodies = [
Sun(mass=1.5e30, init_position=[849597870.700, 0, 0], init_velocity=[0, 7.0, 0],
size_scale=5e1, texture="sun1.jpg"), # 太阳放大 100 倍
Sun(mass=2e30, init_position=[0, 0, 0], init_velocity=[0, -8.0, 0],
size_scale=5e1, texture="sun2.jpg"), # 太阳放大 100 倍
Earth(init_position=[0, 349597870.700, 0], init_velocity=[10.50, 0, 0],
size_scale=4e3, distance_scale=1), # 地球放大 4000 倍,距离保持不变
]
# 使用 mayavi 查看的运行效果
# mayavi_run(bodies, SECONDS_PER_WEEK, view_azimuth=0)
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
ursina_run(bodies, SECONDS_PER_YEAR, position=(0, 2 * AU, -5 * AU), show_trail=True)
\ No newline at end of file
# -*- coding:utf-8 -*-
# title :ursina模拟器(支持天体json文件的读取)
# description :ursina模拟器(支持天体json文件的读取)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from bodies import Body
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_MONTH, SECONDS_PER_YEAR, AU
from scenes.func import ursina_run
if __name__ == '__main__':
# TODO: 去掉注释,太阳演示
# bodies, params = Body.build_bodies_from_json('../data/sun.json')
# TODO: 去掉注释,太阳和地球演示
# bodies, params = Body.build_bodies_from_json('../data/sun_earth.json')
# TODO: 去掉注释,在地球上看月相演示变化过程演示
# bodies, params = Body.build_bodies_from_json('../data/sun_earth_moon.json')
# TODO: 去掉注释,完美数据的三体模型的演示01(等边三角形)
bodies, params = Body.build_bodies_from_json('../data/tri_bodies_sim_perfect_01.json')
# TODO: 去掉注释,完美数据的三体模型的演示02(等边三角形)
# bodies, params = Body.build_bodies_from_json('../data/tri_bodies_sim_perfect_02.json')
# TODO: 去掉注释,完美数据的三体模型的演示03(等边三角形)
# bodies, params = Body.build_bodies_from_json('../data/tri_bodies_sim_perfect_03.json')
# TODO: 去掉注释,引力弹弓的演示
# bodies, params = Body.build_bodies_from_json('../data/gravity_slingshot.json')
dt = params["dt"] if "dt" in params else SECONDS_PER_YEAR
position = params["position"] if "position" in params else (0, 0, 0)
show_trail = params["show_trail"] if "show_trail" in params else True
# 使用 ursina 查看的运行效果
# 常用快捷键: P:运行和暂停 O:重新开始 I:显示天体轨迹
# position = 左-右+、上+下-、前+后-
json_file = None # 指定 json_file 保存路径,则会将模拟环境天体数据保存到该json文件中
ursina_run(bodies, dt, position=position, save_as_json=json_file, show_trail=show_trail)
# -*- coding:utf-8 -*-
# title :模拟器用功能库
# description :模拟器用功能库
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
import matplotlib.pyplot as plt
import matplotlib.colors as mcolors
from matplotlib import ticker
from common.consts import SECONDS_PER_WEEK
from common.system import System
COSMIC_BG_COLOR = "#002563"
COSMIC_FORE_COLOR = "white"
def get_default_colors(styles={}):
"""
:param styles:
:return:
"""
bg_color = styles["bg_color"] if "bg_color" in styles else "white" # COSMIC_BG_COLOR
fore_color = styles["fore_color"] if "fore_color" in styles else "black" # COSMIC_FORE_COLOR
# bg_color = styles["bg_color"] if "bg_color" in styles else COSMIC_BG_COLOR
# fore_color = styles["fore_color"] if "fore_color" in styles else COSMIC_FORE_COLOR
styles["bg_color"] = bg_color
styles["fore_color"] = fore_color
return bg_color, fore_color
def create_fig_ax(styles={}):
"""
:param styles:
:return:
"""
bg_color, fore_color = get_default_colors(styles)
plt.rcParams['patch.facecolor'] = bg_color
plt.rcParams['xtick.color'] = fore_color
plt.rcParams['ytick.color'] = fore_color
plt.rcParams['axes.labelcolor'] = fore_color
plt.rcParams['axes.edgecolor'] = fore_color
plt.rcParams['axes.facecolor'] = fore_color
plt.rcParams['figure.facecolor'] = fore_color
if bg_color is None:
fig = plt.figure('天体模拟运行效果', figsize=(20, 12))
else:
fig = plt.figure('天体模拟运行效果', figsize=(20, 12), facecolor=bg_color)
ax = fig.gca(projection="3d")
# 调整子图区域
plt.subplots_adjust(left=-0.8, right=1.8, bottom=0.0, top=0.95)
# # 设置窗口大小
# fig.set_size_inches(20, 12)
# ax.set_box_aspect([2, 1, 1]) # 将三个轴设置为等比例缩放
return fig, ax
def update_ax_styles(ax, styles={}):
"""
:param ax:
:param styles:
:return:
"""
plt.cla()
bg_color, fore_color = get_default_colors(styles)
# # 设置 x 轴刻度
# ax.xaxis.set_major_locator(ticker.MultipleLocator(base=5))
# ax.xaxis.set_minor_locator(ticker.MultipleLocator(base=0.01))
#
# # 设置 y 轴刻度
# ax.yaxis.set_major_locator(ticker.MultipleLocator(base=5))
# ax.yaxis.set_minor_locator(ticker.MultipleLocator(base=0.01))
#
# # 设置 z 轴刻度
# ax.zaxis.set_major_locator(ticker.MultipleLocator(base=5))
# ax.zaxis.set_minor_locator(ticker.MultipleLocator(base=0.01))
# ax.w_xaxis.line.set_color(fore_color)
# ax.w_yaxis.line.set_color(fore_color)
# ax.w_zaxis.line.set_color(fore_color)
# ax.xaxis.label.set_color(fore_color)
# ax.yaxis.label.set_color(fore_color)
# ax.zaxis.label.set_color(fore_color)
# ax.tick_params(axis='both', colors=fore_color)
# ax.tick_params(axis='x', colors=fore_color) # only affects
# ax.tick_params(axis='y', colors=fore_color) # tick labels
# ax.tick_params(axis='z', colors=fore_color) # not tick marks
# ax.margins(x=1e20, y=None, z=None, tight=False)
ax.patch.set_color(bg_color) # 设置 ax 区域背景颜色
bg_color = mcolors.to_rgba(bg_color)
fore_color_alpha = mcolors.to_rgba(fore_color, alpha=0.3)
# bg_color = (0.2, 0.2, 1.0, 1.0)
# 设置网格背景颜色
# ax.grid(color=fore_color)
# ax.grid(color=fore_color, linestyle='dashed', linewidth=1, alpha=0.1)
ax.xaxis._axinfo["grid"]['color'] = fore_color_alpha
ax.yaxis._axinfo["grid"]['color'] = fore_color_alpha
ax.zaxis._axinfo["grid"]['color'] = fore_color_alpha
ax.xaxis._axinfo["grid"]['linestyle'] = 'dashed'
ax.yaxis._axinfo["grid"]['linestyle'] = 'dashed'
ax.zaxis._axinfo["grid"]['linestyle'] = 'dashed'
ax.xaxis._axinfo["grid"]['linewidth'] = 0.5
ax.yaxis._axinfo["grid"]['linewidth'] = 0.5
ax.zaxis._axinfo["grid"]['linewidth'] = 0.5
# ax.xaxis._axinfo["grid"]['alpha'] = 0.1
# ax.yaxis._axinfo["grid"]['alpha'] = 0.1
# ax.zaxis._axinfo["grid"]['alpha'] = 0.1
ax.w_xaxis.set_pane_color(bg_color)
ax.w_yaxis.set_pane_color(bg_color)
ax.w_zaxis.set_pane_color(bg_color)
# ax.axes.yaxis.grid(color='red', linestyle='--', linewidth=1, alpha=1)
# ax.axes.yaxis.gridlines
# ax.set_bgcolor('red')
# ax.xaxis.grid(color='r', linestyle='--', linewidth=1, alpha=1)
# ax.axes.yaxis.grid(color='r', linestyle='--', linewidth=1, alpha=1)
# ax.spines['right'].set_color('blue')
# ax.spines['top'].set_color('none')
# axs0=plt.subplot(221,axisbg='#FFDAB9')
ax.set_title('天体模拟运行效果', loc='center', pad=0, fontsize="24", color=fore_color)
# ax.set_title('Title', )
ax.set_xlabel('X(天文单位:AU)', fontsize="18", color=fore_color)
ax.set_ylabel('Y(天文单位:AU)', fontsize="18", color=fore_color)
ax.set_zlabel('Z(天文单位:AU)', fontsize="18", color=fore_color)
# -*- coding:utf-8 -*-
# title :manim天体运行模拟器
# description :manim天体运行模拟器
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# manim version :Manim Community v0.17.2
# ==============================================================================
# 环境安装教程参考:
# https://blog.csdn.net/FRIGIDWINTER/article/details/121526956
import os
from manim import *
from manim import *
from manim.mobject.three_d import Sphere
from manim.three_d.light import *
from manim.mobject.opengl import *
from PIL import Image
class TexturedSphere(ThreeDVMobject):
def __init__(self, radius=1, u_min=0, u_max=1, v_min=0, v_max=1, **kwargs):
self.radius = radius
self.u_min = u_min
self.u_max = u_max
self.v_min = v_min
self.v_max = v_max
super().__init__(**kwargs)
def init_points(self):
sphere = Sphere(radius=self.radius).mesh
for i in range(len(sphere[0])):
u, v = sphere[0][i], sphere[1][i]
self.add_point(self.radius * np.array([
np.cos(u) * np.sin(v),
np.sin(u) * np.sin(v),
np.cos(v)
]))
self.add_tex_coords([
interpolate(self.u_min, self.u_max, u / (2 * np.pi)),
interpolate(self.v_min, self.v_max, v / np.pi)
])
self.set_color_by_tex()
def set_color_by_tex(self, texture_file='saturn.jpg'):
texture_path = f'textures/{texture_file}'
texture = Image.open(texture_path).transpose(Image.FLIP_TOP_BOTTOM).convert('RGBA')
self.set_texture(texture)
class TexturedSaturn(Scene):
def construct(self):
sphere = TexturedSphere(radius=2.5, texture_file='saturn.jpg')
rings = TexturedSphere(radius=3.5, texture_file='saturnRings.jpg', u_min=0.2, u_max=0.8, v_min=0.2, v_max=0.8)
rings.set_rotation(right=PI / 2)
rings.set_shade_in_3d(True)
rings.set_opacity(0.8)
light = ThreeDPointLight(
location=5 * OUT + 5 * RIGHT + 5 * UP,
color=WHITE,
)
ambient = ThreeDAmbientLight(color="#444444")
self.add(light, ambient, sphere, rings)
self.set_camera_orientation(phi=75 * DEGREES, theta=-30 * DEGREES)
self.begin_ambient_camera_rotation(rate=0.2)
self.wait(20)
self.stop_ambient_camera_rotation()
if __name__ == '__main__':
os.system("manim -pqh manim_simulator.py TexturedSaturn")
# -*- coding:utf-8 -*-
# title :Mayavi天体运行模拟器
# description :Mayavi天体运行模拟器
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# mayavi version :4.8.1
# ==============================================================================
from mayavi import mlab
from simulators.simulator import Simulator
from common.system import System
from simulators.views.mayavi_view import MayaviView
class MayaviSimulator(Simulator):
"""
Mayavi天体运行模拟器
"""
def __init__(self, bodies_sys: System):
super().__init__(bodies_sys, MayaviView)
@mlab.animate(delay=100, ui=True)
def run(self, dt, **kwargs):
f = mlab.gcf()
while True:
self.evolve(dt)
f.scene.render()
yield
if __name__ == '__main__':
from scenes.func import mayavi_run
from bodies import Sun, Earth
from common.consts import SECONDS_PER_WEEK
"""
3个太阳、1个地球
"""
bodies = [
Sun(name='太阳1', mass=1.5e30, init_position=[849597870.700, 0, 0], init_velocity=[0, 7.0, 0],
size_scale=5e1, texture="sun1.jpg"), # 太阳放大 100 倍
Sun(name='太阳2', mass=2e30, init_position=[0, 0, 0], init_velocity=[0, -8.0, 0],
size_scale=5e1, texture="sun2.jpg"), # 太阳放大 100 倍
Sun(name='太阳3', mass=2.5e30, init_position=[0, -849597870.700, 0], init_velocity=[18.0, 0, 0],
size_scale=5e1, texture="sun2.jpg"), # 太阳放大 100 倍
Earth(name='地球', init_position=[0, -349597870.700, 0], init_velocity=[15.50, 0, 0],
size_scale=4e3, distance_scale=1), # 地球放大 4000 倍,距离保持不变
]
mayavi_run(bodies, SECONDS_PER_WEEK)
# -*- coding:utf-8 -*-
# title :matplotlib天体运行模拟器
# description :matplotlib天体运行模拟器
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import matplotlib.colors as mcolors
from simulators.simulator import Simulator
from common.system import System
from common.consts import AU
from simulators.views.mpl_view import MplView
from simulators.func import create_fig_ax, update_ax_styles
import numpy as np
import copy
plt.rcParams['font.sans-serif'] = ['SimHei'] # 步骤一(替换默认sans-serif字体)
plt.rcParams['axes.unicode_minus'] = False # 步骤二(解决坐标轴负数的负号显示问题)
class MplSimulator(Simulator):
"""
matplotlib天体运行模拟器
"""
def __init__(self, bodies_sys: System):
super().__init__(bodies_sys, MplView)
def save_as_gif(self, dt, gif_max_frame=200, gif_file_name='bodies_run.gif', styles={}):
"""
保存 GIF 文件
:param dt: 单位:秒,按时间差进行演变,值越小越精确,但演变速度会慢。
:param gif_max_frame: 导出的 gif 文件的画面帧数
:param gif_file_name: 导出的 gif 文件名
:return:
"""
fig, ax = create_fig_ax()
views_frames = []
for i in range(gif_max_frame):
self.evolve(dt)
body_views = copy.deepcopy(self.body_views)
views_frames.append(body_views)
def update(num):
body_views = views_frames[num]
print("\rGIF 生成进度:%d/%d %.2f" % (num + 1, gif_max_frame, ((num + 1) / gif_max_frame) * 100) + "%", end='')
return self.show_figure(ax, body_views, pause=0, update_ax=update_ax_styles, styles=styles)
ani = animation.FuncAnimation(fig=fig, func=update, frames=np.arange(0, gif_max_frame), interval=1)
ani.save(gif_file_name)
def run(self, dt, **kwargs):
"""
:param dt: 单位:秒,按时间差进行演变,值越小越精确,但演变速度会慢。
:param kwargs:
gif_file_name: 导出的 gif 文件名,如果为空,则显示动画
gif_max_frame: 导出的 gif 文件的画面帧数
:return:
"""
gif_file_name = kwargs["gif_file_name"] if "gif_file_name" in kwargs else None
gif_max_frame = kwargs["gif_max_frame"] if "gif_max_frame" in kwargs else None
styles = kwargs["styles"] if "styles" in kwargs else {}
if gif_file_name is not None:
self.save_as_gif(dt, gif_max_frame=gif_max_frame, gif_file_name=gif_file_name, styles=styles)
return
fig, ax = create_fig_ax()
# TODO: 注意:显示动态图,需先进行以下设置:
# Pycharm::File –> Settings –> Tools –> Python Scientific –> Show plots in tool window(取消打勾)
while True:
self.evolve(dt)
body_views = copy.deepcopy(self.body_views)
self.show_figure(ax, body_views, pause=0.1, update_ax=update_ax_styles, styles=styles)
def show_figure(self, ax, bodies, pause=0.1, update_ax=None, styles={}):
"""
:param ax:
:param bodies:
:param pause:
:return:
"""
if update_ax is not None:
# 更新 ax
update_ax(ax, styles)
for idx, body in enumerate(bodies):
if hasattr(body, "torus_stars"):
# 暂不支持环状小行星群
continue
if body.is_fixed_star:
color = 'red'
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)
pos = body.position / AU
# 天体
ax.scatter(pos[0], pos[1], pos[2], color=color, s=size, alpha=0.8)
# for _his_pos in body.his_position():
# ax.scatter3D(_his_pos[0], _his_pos[1], _his_pos[2], color=color, alpha=0.5)
# ax.scatter(his_pos[0], his_pos[1], his_pos[2], color=colors[idx], s=10)
his_pos = np.array(body.his_position) / AU
tail_len = len(his_pos)
if tail_len > 1:
_his_pos = list(zip(*his_pos))
# 历史轨迹线
ax.plot3D(_his_pos[0], _his_pos[1], _his_pos[2], color=color, alpha=0.5)
z_range = ax.get_zlim()[1] - ax.get_zlim()[0]
ax.text(pos[0], pos[1], pos[2] + size * (z_range / 5000), s=body.name, color=color, fontsize=12)
if pause > 0:
plt.pause(pause)
if __name__ == '__main__':
from scenes.func import mpl_run
from bodies import Sun, Earth
from common.consts import SECONDS_PER_WEEK
"""
3个太阳、1个地球
"""
bodies = [
Sun(name='太阳1', mass=1.5e30, init_position=[849597870.700, 0, 0], init_velocity=[0, 7.0, 0],
size_scale=5e1, texture="sun1.jpg"), # 太阳放大 100 倍
Sun(name='太阳2', mass=2e30, init_position=[0, 0, 0], init_velocity=[0, -8.0, 0],
size_scale=5e1, texture="sun2.jpg"), # 太阳放大 100 倍
Sun(name='太阳3', mass=2.5e30, init_position=[0, -849597870.700, 0], init_velocity=[18.0, 0, 0],
size_scale=5e1, texture="sun2.jpg"), # 太阳放大 100 倍
Earth(name='地球', init_position=[0, -349597870.700, 0], init_velocity=[15.50, 0, 0],
size_scale=4e3, distance_scale=1), # 地球放大 4000 倍,距离保持不变
]
# 保存 GIF,参数为指定保存 GIF 文件以及文件的画面帧数
gif_file_name, gif_max_frame = 'bodies_run.gif', 100
# 只显示动画,不保存 GIF 文件。注释掉以下代码,则使用上面的参数
# TODO: 注意:显示动态图,需先进行以下设置:
# Pycharm::File –> Settings –> Tools –> Python Scientific –> Show plots in tool window(取消打勾)
gif_file_name, gif_max_frame = None, None
mpl_run(bodies, SECONDS_PER_WEEK, gif_file_name, gif_max_frame)
# -*- coding:utf-8 -*-
# title :pyglet天体运行模拟器
# description :pyglet天体运行模拟器
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
# pip install -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com pyglet
# -*- coding:utf-8 -*-
# title :天体运行模拟器
# description :天体运行模拟器
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from abc import ABCMeta, abstractmethod
from common.system import System
class Simulator(metaclass=ABCMeta):
"""
天体运行模拟器
"""
def __init__(self, bodies_sys: System, viewer_type: type):
"""
:param bodies_sys: 天体系统
:param viewer_type: BodyViewer类型
"""
self.body_views = []
self.bodies_sys = bodies_sys
self.init_views(viewer_type)
def init_views(self, viewer_type: type):
"""
:param viewer_type: BodyViewer类型
:return:
"""
for body in self.bodies_sys.bodies:
view = viewer_type(body, self.bodies_sys)
self.body_views.append(view)
def evolve(self, dt: int):
"""
单位:秒,按时间差进行演变,值越小越精确,但演变速度会慢。
:param dt: 时间差(秒)
:return:
"""
self.bodies_sys.evolve(dt)
for idx, view in enumerate(self.body_views):
body = self.bodies_sys.bodies[idx]
body.dt = dt
view.appeared = body.appeared
if not view.appeared:
view.disappear()
continue
view.position = body.position * body.distance_scale
view.name = body.name
view.mass = body.mass
view.acceleration = body.acceleration
view.velocity = body.velocity
# viewer.volume = body.volume
view.raduis = body.raduis
view.his_position = body.his_position()
view.is_fixed_star = body.is_fixed_star
view.has_rings = body.has_rings
view.size_scale = body.size_scale
view.distance_scale = body.distance_scale
view.update()
self.bodies_sys.bodies = list(filter(lambda b: b.appeared, self.bodies_sys.bodies))
self.body_views = list(filter(lambda b: b.appeared, self.body_views))
@abstractmethod
def run(self, dt: int, **kwargs):
"""
按时间差运行,值越小越精确,但演变速度会慢。
:param dt: 时间差(秒)
:return:
"""
pass
# -*- coding:utf-8 -*-
# title :ursina天体运行模拟器UI控制
# description :ursina天体运行模拟器UI控制
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from ursina import Ursina, window, Entity, Grid, Mesh, camera, Text, application, color, mouse, Vec2, Vec3, \
load_texture, held_keys, Button, ButtonList, destroy, scene, distance, Sequence, Wait, Func
from ursina.prefabs.first_person_controller import FirstPersonController
from common.consts import SECONDS_PER_HOUR, SECONDS_PER_HALF_DAY, \
SECONDS_PER_DAY, SECONDS_PER_WEEK, SECONDS_PER_MONTH, SECONDS_PER_YEAR
from common.consts import AU
from simulators.ursina.ui.ui_panel import UiPanel
from simulators.ursina.ui_component import UiSlider, SwithButton, UiButton, Buttons
from simulators.ursina.ursina_config import UrsinaConfig
from simulators.ursina.ursina_event import UrsinaEvent
from ursina import WindowPanel, InputField, Button, Slider, ButtonGroup, Panel, invoke
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]+[鼠标右键],按[空格]更多控制"
key_info = Text(text=key_info_str, font=UrsinaConfig.CN_FONT, position=(-1, 0.5), origin=(-1, 1),
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 == "周":
UrsinaConfig.seconds_per = SECONDS_PER_WEEK
elif self.ui.sec_per_time_switch.value == "月":
UrsinaConfig.seconds_per = SECONDS_PER_MONTH
elif self.ui.sec_per_time_switch.value == "年":
UrsinaConfig.seconds_per = SECONDS_PER_YEAR
elif self.ui.sec_per_time_switch.value == "十年":
UrsinaConfig.seconds_per = SECONDS_PER_YEAR * 10
elif self.ui.sec_per_time_switch.value == "百年":
UrsinaConfig.seconds_per = SECONDS_PER_YEAR * 100
else:
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_entity2(self, camera_pos: Vec3, entity_pos: Vec3, _distance: float) -> Vec3:
# 计算摄像机到实体的向量
direction = entity_pos - camera_pos
# 计算当前距离
current_distance = direction.length()
# 如果当前距离已经小于等于要求的距离,则直接返回实体坐标
if current_distance <= _distance:
return camera_pos
# 计算需要移动的距离
_distance = current_distance - _distance
# 根据需要移动的距离计算移动向量
move_vector = direction.normalized() * _distance
# 返回摄像机移动后的坐标
return camera_pos + move_vector
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 search_bodies_button_list_click(self, item):
"""
@param item:
@return:
"""
if item is not None:
# TODO: 先找到位置,确定摄像机的位置
try:
d = item.planet.scale_x * 20
self.move_camera_to_entity(item.planet, d)
except Exception as e:
self.ui.show_message(f"{item}飞不见了")
self.search_bodies_button_list_close()
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()
if len(results) > 0:
sub_name, bodies = results[0]
if len(bodies) == 0:
self.ui.show_message("天体都飞不见了,请重新运行。")
return
button_dict = {"[关闭] == 寻找天体 ==": lambda: self.search_bodies_button_list_click(None)}
camera = scene.camera
for body in bodies:
def callback_action(b=body):
self.search_bodies_button_list_click(b)
if body.appeared:
distance_to_entity = distance(body.planet, camera)
d = distance_to_entity / UrsinaConfig.SCALE_FACTOR / AU
name = f"{body.name}\t距离:{d:.4f}天文单位"
button_dict[name] = callback_action
else:
if hasattr(self, "search_bodies_button_list"):
self.search_bodies_button_list_close()
name = f"{body.name}\t距离太远,找不到了"
button_dict[name] = lambda: self.search_bodies_button_list_click(None)
if hasattr(self, "search_bodies_button_list"):
self.search_bodies_button_list_close()
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
if paused: # 如果是暂停状态,先不暂停,等重新开始后再暂停
application.paused = False
UrsinaEvent.on_reset()
if paused:
def application_paused():
application.paused = True
UrsinaEvent.on_application_run_callback_subscription(application_paused)
# def on_buttons_changed(self):
# if self.ui.buttons.value == "寻找":
# self.on_searching_bodies_click()
# elif self.ui.buttons.value == "重启":
# self.on_reset_button_click()
def on_off_switch_changed(self):
if self.ui.on_off_switch.value == self.ui.pause_button_text:
self.ui.on_off_switch.selected_color = color.green
application.paused = True
for c in self.ui.children:
if not c.ignore_paused:
# c.enabled = True
c.disabled = False
else:
self.ui.on_off_switch.selected_color = color.red
application.paused = False
for c in self.ui.children:
if not c.ignore_paused:
# c.enabled = True
c.disabled = False
def on_slider_trail_length_changed(self):
UrsinaConfig.trail_length = int(self.ui.slider_trail_length.value)
def on_slider_control_speed_changed(self):
application.time_scale = self.ui.slider_control_speed_factor.value
def on_slider_body_spin_changed(self):
UrsinaConfig.body_spin_factor = self.ui.slider_body_spin_factor.value
def on_slider_body_size_changed(self):
UrsinaConfig.body_size_factor = self.ui.slider_body_size_factor.value
def on_slider_run_speed_changed(self):
UrsinaConfig.run_speed_factor = self.ui.slider_run_speed_factor.value
def settings_handler_input(self, key):
"""
@param key:
@return:
"""
import sys
if key == "escape":
sys.exit()
# print(key)
elif key == 'space':
self.ui.enabled = not self.ui.enabled
elif key == 'left mouse down':
print(key)
elif key == 'y': # 寻找天体
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': # 重新开始
self.on_reset_button_click()
elif key == 'i': # 拖尾开关
if self.ui.on_off_trail.value == self.ui.trail_button_text:
self.ui.on_off_trail.value = self.ui.no_trail_button_text
else:
self.ui.on_off_trail.value = self.ui.trail_button_text
self.on_off_trail_changed()
elif key == 'p': # 开始、暂停
if self.ui.on_off_switch.value == self.ui.pause_button_text:
self.ui.on_off_switch.value = self.ui.start_button_text
else:
self.ui.on_off_switch.value = self.ui.pause_button_text
self.on_off_switch_changed()
elif key == '+' or key == "= up":
self.slider_increase(self.ui.slider_run_speed_factor)
elif key == '-' or key == "- up":
self.slider_decrease(self.ui.slider_run_speed_factor)
elif key == 'n':
import math
# min=0.01, max=20
# # ', up' < > '. up' n m
self.ui.slider_control_speed_factor.current_step = self.ui.slider_control_speed_factor.value/2
self.slider_decrease(self.ui.slider_control_speed_factor, self.ui.slider_control_speed_factor.current_step)
print(self.ui.slider_control_speed_factor.current_step)
elif key == 'm':
self.ui.slider_control_speed_factor.current_step = self.ui.slider_control_speed_factor.value
self.slider_increase(self.ui.slider_control_speed_factor, self.ui.slider_control_speed_factor.current_step)
print(self.ui.slider_control_speed_factor.current_step)
def slider_increase(self, slider, step=50):
"""
@param slider:
@param step:
@return:
"""
run_speed_factor = slider.value + step
if run_speed_factor > slider.max:
run_speed_factor = slider.max
slider.value = run_speed_factor
slider.knob.drop()
def slider_decrease(self, slider, step=50):
"""
@param slider:
@param step:
@return:
"""
value = slider.value - step
if value < slider.min:
value = slider.min
slider.value = value
slider.knob.drop()
# -*- coding:utf-8 -*-
# title :ursina天体运行模拟器UI控制
# description :ursina天体运行模拟器UI控制
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from ursina import Ursina, window, Entity, Grid, Mesh, camera, Text, application, color, mouse, Vec2, Vec3, \
load_texture, held_keys, Button, ButtonList, destroy, scene, distance, Sequence, Wait, Func
from ursina.prefabs.first_person_controller import FirstPersonController
from common.consts import SECONDS_PER_HOUR, SECONDS_PER_HALF_DAY, \
SECONDS_PER_DAY, SECONDS_PER_WEEK, SECONDS_PER_MONTH, SECONDS_PER_YEAR
from common.consts import AU
from simulators.ursina.ui.control_handler import ControlHandler
from simulators.ursina.ui.ui_panel import UiPanel
from simulators.ursina.ui_component import UiSlider, SwithButton, UiButton, Buttons
from simulators.ursina.ursina_config import UrsinaConfig
from simulators.ursina.ursina_event import UrsinaEvent
from ursina import WindowPanel, InputField, Button, Slider, ButtonGroup, Panel, invoke
class ControlUI(UiPanel):
"""
控制面板界面
"""
def component_init(self):
self.start_button_text = "●" # 》●▲○◎
self.pause_button_text = "〓" # 〓 || ‖
self.no_trail_button_text = "○ "
self.trail_button_text = "○--"
self.slider_body_spin_factor = UiSlider(text='自转速度', min=0.01, max=5, default=1)
self.slider_body_size_factor = UiSlider(text='天体缩放', min=0.1, max=100, step=0.1, default=1)
self.slider_run_speed_factor = UiSlider(text="运行速度", min=0.01, max=80, default=1)
self.slider_control_speed_factor = UiSlider(text="控制速度", min=0.01, max=20,
step=0.1, default=application.time_scale)
self.slider_trail_length = UiSlider(text="拖尾长度", min=30, max=500, step=10, default=UrsinaConfig.trail_length)
self.on_off_switch = SwithButton((self.pause_button_text,
self.start_button_text),
default=self.start_button_text,
tooltips=('暂停(P)', '运行(P)'))
self.on_off_switch.selected_color = color.red
self.sec_per_time_switch = SwithButton(("默认", "天", "周", "月", "年", "十年", "百年"),
default="默认",
tooltips=("系统默认", "每秒相当于1天", "每秒相当于1周",
"每秒相当于1个月",
"每秒相当于1年", "每秒相当于十年", "每秒相当于1百年"))
self.on_off_trail = SwithButton((self.no_trail_button_text, self.trail_button_text),
default=self.no_trail_button_text,
tooltips=('天体运行无轨迹', '天体运行有拖尾轨迹'))
self.point_button = UiButton(text='寻找天体(Y)', on_click=None)
self.reset_button = UiButton(text='重新开始(O)', on_click=None)
content = (
# InputField(name='name_field'),
# Button(text='Submit', color=color.azure),
self.point_button,
self.reset_button,
# self.buttons,
self.sec_per_time_switch,
self.on_off_switch,
self.on_off_trail,
self.slider_trail_length,
self.slider_body_size_factor,
self.slider_body_spin_factor,
self.slider_run_speed_factor,
self.slider_control_speed_factor
)
return content
def after_component_init(self):
self.sec_per_time_switch.x = -0.4
self.on_off_switch.x = 0.2
self.on_off_trail.x = 0.2 # -0.4
def event_handler_init(self):
self.slider_body_size_factor.on_value_changed = self.handler.on_slider_body_size_changed
self.slider_body_spin_factor.on_value_changed = self.handler.on_slider_body_spin_changed
self.slider_run_speed_factor.on_value_changed = self.handler.on_slider_run_speed_changed
self.slider_control_speed_factor.on_value_changed = self.handler.on_slider_control_speed_changed
self.slider_trail_length.on_value_changed = self.handler.on_slider_trail_length_changed
self.on_off_trail.on_value_changed = self.handler.on_off_trail_changed
self.on_off_switch.on_value_changed = self.handler.on_off_switch_changed
self.point_button.on_click = self.handler.on_searching_bodies_click
self.reset_button.on_click = self.handler.on_reset_button_click
self.sec_per_time_switch.on_value_changed = self.handler.sec_per_time_switch_changed
if __name__ == '__main__':
app = Ursina()
ctl = ControlUI(ControlHandler(), position=(0.6, 0.5), enabled=True)
app.run()
# -*- coding:utf-8 -*-
# title :ursina天体运行模拟器UI控制
# description :ursina天体运行模拟器UI控制
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
class EventHandler:
def __init__(self):
self.ui = None
self.handler_input_init()
def handler_input_init(self):
"""
@return:
"""
pass
# -*- coding:utf-8 -*-
# title :ursina天体运行模拟器UI控制
# description :ursina天体运行模拟器UI控制
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from ursina import Ursina, window, Entity, Grid, Mesh, camera, Text, application, color, mouse, Vec2, Vec3, \
load_texture, held_keys, Button, ButtonList, destroy, scene, distance, Sequence, Wait, Func
from ursina.prefabs.first_person_controller import FirstPersonController
from common.consts import SECONDS_PER_HOUR, SECONDS_PER_HALF_DAY, \
SECONDS_PER_DAY, SECONDS_PER_WEEK, SECONDS_PER_MONTH, SECONDS_PER_YEAR
from common.consts import AU
from simulators.ursina.ui_component import UiSlider, SwithButton, UiButton, Buttons
from simulators.ursina.ursina_config import UrsinaConfig
from simulators.ursina.ursina_event import UrsinaEvent
from ursina import WindowPanel, InputField, Button, Slider, ButtonGroup, Panel, invoke
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
self.x = position[0] # wp.scale_x + 0.1
self.enabled = enabled
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: 定义显示消息框关闭时间
:return:
"""
# 创建消息框
message_box = Text(text=message, font=UrsinaConfig.CN_FONT, background=True, origin=(0, 0), y=.25)
close_time = close_time * application.time_scale
# 定义关闭函数
def close_message():
destroy(message_box)
s = Sequence(
Wait(close_time),
Func(close_message)
)
s.start()
# -*- coding:utf-8 -*-
# title :ursina UI组件
# description :ursina UI组件
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from ursina import Ursina, window, Entity, Grid, Mesh, camera, Text, application, color, mouse, Vec2, Vec3, \
load_texture, held_keys, Button, Tooltip
from ursina.prefabs.first_person_controller import FirstPersonController
from simulators.ursina.ursina_config import UrsinaConfig
from simulators.ursina.ursina_event import UrsinaEvent
from ursina import WindowPanel, InputField, Button, Slider, ButtonGroup
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,
height=Text.size,
y=-.6,
step=step,
min=min,
max=max,
default=default,
color=color.rgba(0.0, 0.0, 0.0, 0.5),
ignore_paused=False,
dynamic=True)
# self.label.scale *= 8/10
self.label.font = UrsinaConfig.CN_FONT
# self.knob.text_entity.font = ""
# self.knob.text_entity.scale *= 8/10
# self.height *= 8/10
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,
color=color.rgba(0.0, 0.0, 0.0, 0.5))
# self.label.scale = 0.8
# self.label.font = UrsinaConfig.CN_FONT
for i, button in enumerate(self.buttons):
button.text_entity.font = UrsinaConfig.CN_FONT
if tooltips is not None:
if len(tooltips) > i:
tooltip = Tooltip(tooltips[i])
tooltip.font = UrsinaConfig.CN_FONT
button.tooltip = tooltip
self.x = -0.5
class Buttons(ButtonGroup):
"""
"""
def __init__(self, options, default=None, tooltips=None):
min_selection = len(options)
super().__init__(options, min_selection=1, default=default,
color=color.rgba(0.1, 0.6, 0.1, 1.0), ignore_paused=True,
selected_color=color.rgba(0.0, 0.0, 0.0, 0.5))
# self.label.scale = 0.8
# self.label.font = UrsinaConfig.CN_FONT\
for i, button in enumerate(self.buttons):
button.text_entity.font = UrsinaConfig.CN_FONT
# button.scale_x = 2
if tooltips is not None:
if len(tooltips) > i:
tooltip = Tooltip(tooltips[i])
tooltip.font = UrsinaConfig.CN_FONT
button.tooltip = tooltip
self.x = -0.5
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),
ignore_paused=False)
self.text_entity.font = UrsinaConfig.CN_FONT
# -*- coding:utf-8 -*-
# title :ursina天体运行模拟器
# description :ursina天体运行模拟器
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
# pip install -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com ursina
class UrsinaConfig:
# 常量定义
# 天体缩放的因子(不能太大,否则无法容得下大数量级的天体)调整 5e-7 最佳
__SCALE_FACTOR = 5e-7
auto_scale_factor = 1.0 # __SCALE_FACTOR 不能满足,需要自动进行调整
# 旋转因子为1,则为正常的转速
ROTATION_SPEED_FACTOR = 1.0
# ROTATION_SPEED_FACTOR = 0.01
# 中文字体(微软雅黑)
CN_FONT = "msyhl.ttc" # 'simsun.ttc' 仿宋体
# CN_FONT = 'simsun.ttc' # 仿宋体
# 速度的倍数
__run_speed_factor = 1.0
# 天体自旋倍数
__body_spin_factor = 1.0
# 摄像机
__camera_factor = 1.0
__on_reset_funcs = []
show_trail = False
# 拖尾球体的数量
trail_length = 100
# 默认秒数(0表示默认)
seconds_per = 0
# # 控制摄像机动作速度(天体越大,速度越快,天体越小,速度越慢)
# control_camera_speed = 1
__body_size_factor = 1.0
@classmethod
def init(cls):
# 初始化
cls.run_speed_factor = 1.0
cls.body_spin_factor = 1.0
cls.body_size_factor = 1.0
# 天体缩放的因子(不能太大,否则无法容得下大数量级的天体)调整 5e-7 最佳
cls.SCALE_FACTOR = 5e-7
@property
@classmethod
def SCALE_FACTOR(cls):
return cls.__SCALE_FACTOR * cls.auto_scale_factor
@SCALE_FACTOR.setter
@classmethod
def SCALE_FACTOR(cls, value):
cls.__SCALE_FACTOR = value
@property
@classmethod
def run_speed_factor(cls):
return cls.__run_speed_factor
@run_speed_factor.setter
@classmethod
def run_speed_factor(cls, value):
cls.__run_speed_factor = value
@property
@classmethod
def body_spin_factor(cls):
return cls.__body_spin_factor
@body_spin_factor.setter
@classmethod
def body_spin_factor(cls, value):
cls.__body_spin_factor = value
@property
@classmethod
def body_size_factor(cls):
return cls.__body_size_factor
@body_size_factor.setter
@classmethod
def body_spin_factor(cls, value):
cls.__body_size_factor = value
@classmethod
def on_reset_subscription(cls, fun):
cls.__on_reset_funcs.append(fun)
@classmethod
def on_reset(cls):
for f in cls.__on_reset_funcs:
f()
UrsinaConfig.init()
if __name__ == '__main__':
UrsinaConfig.run_speed_factor = 2.0
print(UrsinaConfig.run_speed_factor)
# -*- coding:utf-8 -*-
# title :ursina天体运行模拟器事件传递
# description :ursina天体运行模拟器事件传递
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
# pip install -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com ursina
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
def on_application_run_callback_subscription(fun):
UrsinaEvent.on_application_run_callback.append(fun)
@staticmethod
def on_searching_bodies_subscription(subscription_name, fun):
UrsinaEvent.on_searching_bodies_funcs.append((subscription_name, fun))
@staticmethod
def on_reset_subscription(fun):
UrsinaEvent.on_reset_funcs.append(fun)
@staticmethod
def on_reset():
for f in UrsinaEvent.on_reset_funcs:
f()
@staticmethod
def on_application_run():
if len(UrsinaEvent.on_application_run_callback) == 0:
return
for f in UrsinaEvent.on_application_run_callback:
f()
UrsinaEvent.on_application_run_callback.clear()
@staticmethod
def on_searching_bodies(**kwargs):
results = []
for subscription_name, fun in UrsinaEvent.on_searching_bodies_funcs:
results.append((subscription_name, fun(**kwargs)))
return results
UrsinaEvent.init()
# -*- coding:utf-8 -*-
# title :ursina天体运行模拟器
# description :ursina天体运行模拟器
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
# pip install -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com ursina
from ursina import Ursina, window, Entity, Grid, Mesh, camera, Text, application, color, mouse, Vec2, Vec3, \
load_texture, held_keys, distance, Audio
from ursina.prefabs.first_person_controller import FirstPersonController
import itertools
from simulators.ursina.ursina_event import UrsinaEvent
# from simulators.ursina.ursina_ui import UrsinaUI
from simulators.ursina.ui.control_ui import ControlUI
from simulators.ursina.ui.control_handler import ControlHandler
from simulators.views.ursina_view import UrsinaView, UrsinaPlayer
from simulators.ursina.ursina_config import UrsinaConfig
from simulators.simulator import Simulator
from common.system import System
import time
import datetime
import math
import os
from ursina import EditorCamera, PointLight, SpotLight, AmbientLight, DirectionalLight
from scenes.func import ursina_run
class WorldGrid(Entity):
"""
创建一个宇宙网格对象
"""
def __init__(self):
super().__init__()
s = 100
grid = Entity(model=Grid(s, s), scale=s * 20, color=color.rgba(255, 255, 255, 20), rotation_x=90,
position=(0, -80, 0))
# 坐标轴
# vertsx = ((0, 0, 0), (10, 0, 0))
# Entity(model=Mesh(vertices=vertsx, mode='line', thickness=3), color=color.cyan).set_light_off()
# vertsyz = [(0, 0, 0), (0, 10, 0), (0, 0, 0), (0, 0, 10)]
# Entity(model=Mesh(vertices=vertsyz, mode='line', thickness=3), color=color.yellow).set_light_off()
grid.set_light_off()
class UrsinaSimulator(Simulator):
"""
Ursina官网: https://www.ursinaengine.org/
"""
def __init__(self, bodies_sys: System):
self.app = Ursina()
# import os
# os.environ['CUDA_VISIBLE_DEVICES'] = '1' # 选择第二个GPU
# self.app = Ursina(window_title='GPU模拟',
# window_kwargs={'vsync': True, 'fullscreen': False, 'borderless': False, 'show_ursina_splash': True,
# 'high_resolution': True})
self.ursina_views = []
window.color = color.black
super().__init__(bodies_sys, UrsinaView)
# ps = ["sun", "mercury", "venus", "earth", "mars", "jupiter", "saturn", "uranus", "neptune"]
# cp = [200, 15, 35, 42, 20, 160, 145, 90, 80]
# x, y, z = 0, 0, 0
for i, view in enumerate(self.body_views):
# pos = tuple(body.position)
# ursina_view = UrsinaView(body)
view.update()
self.ursina_views.append(view)
# planets.append(newPlanet)
# x += cp[i] * 10
self.adjust_system_motion_params()
UrsinaEvent.on_searching_bodies_subscription(type(self).__name__, self.on_searching_bodies)
# def get_bodies_max_distance(self, body_views):
# max_distance = 0
# for b1 in body_views:
# if b1.body.ignore_mass:
# continue
# for b2 in body_views:
# if (b1 is b2) or b2.body.ignore_mass:
# continue
# d = distance(b1.planet, b2.planet)
# if d > max_distance:
# max_distance = d
# return max_distance
def get_bodies_max_distance(self, body_views):
"""
算法优化
:param body_views:
:return:
"""
max_distance = 0
for b1, b2 in itertools.combinations(body_views, 2):
if b1.body.ignore_mass or b2.body.ignore_mass:
continue
d = distance(b1.planet, b2.planet)
if d > max_distance:
max_distance = d
if max_distance == 0:
if len(body_views) > 0:
# 如果最大距离等于0,说明只有一个有效的天体,则以第一个天体的半径为基准
max_distance = pow(body_views[0].planet.scale_x, 3)
return max_distance
def adjust_system_motion_params(self):
"""
调整天体系统运行的参数
:return:
"""
max_distance = self.get_bodies_max_distance(self.body_views)
# 根据天体之间的距离,调整 application.time_scale(控制摄像头运动的速度)
time_scale = round(pow(max_distance, 1 / 4), 2)
if time_scale < 0.01:
time_scale = 0.01
application.time_scale = time_scale
# UrsinaConfig.auto_scale_factor = 1.0e-9
def on_searching_bodies(self, **kwargs):
views = []
for view in self.body_views:
# if view.appeared:
views.append(view)
return views
def check_interval_expired(self):
"""
检查时间间隔是否已过期
:return:
"""
now = datetime.datetime.now()
elapsed_time = now - self.last_time
is_expired = elapsed_time >= self.interval
if is_expired:
self.last_time = now
return is_expired
def check_and_evolve(self):
if self.check_interval_expired():
# 获取配置中的运行速度的因子
run_speed_factor = float(UrsinaConfig.run_speed_factor)
if UrsinaConfig.seconds_per <= 0:
# 配置中,如果为0秒,表示默认开始运行设置的秒数(evolve_dt)
evolve_dt = self.evolve_dt * run_speed_factor
else:
# 配置中,每年、月、天等等有多少秒
evolve_dt = UrsinaConfig.seconds_per * run_speed_factor
# interval_fator 能让更新天体运行状态(位置、速度)更精确
evolve_dt = evolve_dt * self.interval_fator
super().evolve(evolve_dt)
def cosmic_background(self, texture='../textures/cosmic2.jpg'):
"""
加入宇宙背景
:param texture:
:return:
"""
# Add skybox
from ursina import Sky
Sky(texture=texture).scale = 10000
# texture = load_texture(texture)
# sky_dome = Entity(model='sky_dome', texture=texture, scale=10000,
# color=color.white,
# 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 run(self, dt, **kwargs):
window.title = '宇宙模拟器'
# 设置 camera 的裁剪面和位置
# camera.clip_plane_near = 0.01
# camera.fov = 120
# camera.clip_plane_far = 1000
# camera.position = (0, 10, -20)
# camera.rotation_x = -30
# interval_fator 能让更新天体运行状态(位置、速度)更精确
# 设定时间间隔为0.01秒
self.interval_fator = 0.01
self.evolve_dt = dt
# interval 和 last_time 用于检查时间间隔是否已过期
self.interval = datetime.timedelta(seconds=self.interval_fator)
self.last_time = datetime.datetime.now() - datetime.timedelta(seconds=2)
if "light" in kwargs:
if kwargs["light"]:
for v in self.ursina_views:
if v.body.is_fixed_star:
# self.lights = self.create_fixed_star_lights(v.planet)
pass
if "show_grid" in kwargs:
if kwargs["show_grid"]:
WorldGrid()
if "cosmic_bg" in kwargs:
cosmic_bg = kwargs["cosmic_bg"]
if cosmic_bg is None:
# cosmic_bg = '../textures/cosmic1.png'
# cosmic_bg = '../textures/cosmic2.jpg'
cosmic_bg = '../textures/cosmic3.jpg'
import os
if cosmic_bg is not None and os.path.exists(cosmic_bg):
self.cosmic_background(cosmic_bg)
# ui = UrsinaUI()
ctl = ControlUI(ControlHandler(), position=(0.6, 0.5))
EditorCamera(ignore_paused=True)
# 防止打开中文输入法
# self.switch_to_english_input_method()
# file: 指定音乐文件的路径
# loop: 是否循环播放,默认为 True
# autoplay: 是否自动播放,默认为 True
# volume: 音量大小,取值范围为 0.0 到 1.0,默认为 1.0
# pitch: 音调,取值范围为 0.5 到 2.0,默认为 1.0
# time: 指定音乐从何处开始播放,单位为秒,默认为 0.0
# stop_when_done: 音乐播放完毕后是否停止播放,默认为 True
if "bg_music" in kwargs:
bg_music = kwargs["bg_music"]
elif "background_music" in kwargs:
bg_music = kwargs["background_music"]
else:
bg_music = None
if bg_music is None:
# bg_music = "../sounds/universe_04.mp3"
bg_music = "../none"
if os.path.exists(bg_music):
audio = Audio(bg_music, pitch=1, loop=True, autoplay=True)
audio.volume = 0.3
self.app.run()
if __name__ == '__main__':
from bodies import Sun, Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune, Pluto, Moon
from bodies.body import AU
from common.consts import SECONDS_PER_WEEK, SECONDS_PER_DAY, SECONDS_PER_HALF_DAY
"""
3个太阳、1个地球
"""
bodies = [
Sun(name='太阳1', mass=1.5e30, init_position=[849597870.700, 0, 0], init_velocity=[0, 7.0, 0],
size_scale=5e1, texture="sun.png"), # 太阳放大 100 倍
Sun(name='太阳2', mass=2e30, init_position=[0, 0, 0], init_velocity=[0, -8.0, 0],
size_scale=5e1, texture="sun.png"), # 太阳放大 100 倍
Sun(name='太阳3', mass=2.5e30, init_position=[0, -849597870.700, 0], init_velocity=[18.0, 0, 0],
size_scale=5e1, texture="sun.png"), # 太阳放大 100 倍
Earth(name='地球', init_position=[0, -349597870.700, 0], init_velocity=[15.50, 0, 0],
size_scale=4e3, texture="earth.png", distance_scale=1), # 地球放大 4000 倍,距离保持不变
]
# bodies = [
#
# Sun(name='太阳2', mass=1.5e30, init_position=[0, 0, 0], init_velocity=[0, -8.0, 0],
# size_scale=5e1, texture="sun.png"), # 太阳放大 100 倍
# Sun(name='太阳2', mass=1.5e30, init_position=[849597870.700, 0, 0], init_velocity=[0, -8.0, 0],
# size_scale=5e1, texture="sun.png"), # 太阳放大 100 倍
# Sun(name='太阳2', mass=1.5e30, init_position=[0, -849597870.700, 0], init_velocity=[0, -8.0, 0],
# size_scale=5e1, texture="sun.png"), # 太阳放大 100 倍
# Earth(name='地球', mass=5.97237e24, init_position=[0, -349597870.700, 0], init_velocity=[15.50, 0, 0],
# size_scale=4e3, texture="earth.png", distance_scale=1), # 地球放大 4000 倍,距离保持不变
# ]
bodies = [
Sun(size_scale=0.8e2), # 太阳放大 80 倍
Mercury(size_scale=4e3, distance_scale=1.3), # 水星放大 4000 倍,距离放大 1.3 倍
Venus(size_scale=4e3, distance_scale=1.3), # 金星放大 4000 倍,距离放大 1.3 倍
Earth(init_position=[1.12 * AU, 0, 0],
init_velocity=[0, 29.79, 0], size_scale=4e3, distance_scale=1.3), # 地球放大 4000 倍,距离放大 1.3 倍
Moon(init_position=[363104 + 1.12 * AU, 0, 0],
init_velocity=[-9, 29.79 + 1.023, 0], size_scale=4e3, distance_scale=1.3),
Mars(size_scale=4e3, distance_scale=1.3), # 火星放大 4000 倍,距离放大 1.3 倍
Jupiter(size_scale=0.68e3, distance_scale=0.65), # 木星放大 680 倍,距离缩小到真实距离的 0.65
Saturn(size_scale=0.68e3, distance_scale=0.52), # 土星放大 680 倍,距离缩小到真实距离的 0.52
Uranus(size_scale=0.8e3, distance_scale=0.36), # 天王星放大 800 倍,距离缩小到真实距离的 0.36
Neptune(size_scale=1e3, distance_scale=0.27), # 海王星放大 1000 倍,距离缩小到真实距离的 0.27
Pluto(size_scale=10e3, distance_scale=0.23), # 冥王星放大 10000 倍,距离缩小到真实距离的 0.23(从太阳系的行星中排除)
]
ursina_run(bodies, SECONDS_PER_DAY, position=(AU * 2, AU * 2, AU * 3), show_grid=True)
# -*- coding:utf-8 -*-
# title :天体视图
# description :天体视图(天体效果展示用)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from abc import ABCMeta, abstractmethod
from bodies import Body
from common.func import get_dominant_colors
import numpy as np
import os
class BodyView(metaclass=ABCMeta):
"""
天体视图(天体效果展示用)
"""
def __init__(self, body: Body):
self.body = body
self.sphere = None
if self.body.texture is None or self.body.texture == '':
self.color = tuple(np.array(body.color) / 255)
else:
self.texture = self.__find_texture(self.body.texture) # 纹理
if self.texture is None:
self.color = tuple(np.array(body.color) / 255)
else:
self.color = self.__get_texture_main_color(self.texture)
self.appear()
self.position = [None, None, None]
self.name = None
self.mass = None
self.raduis = None
self.velocity = None
self.angle = 0
self.appeared = True
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.position[0], self.position[1], self.position[2], self.velocity)
def __find_texture(self, texture):
"""
尝试在多个路径下寻找纹理图片
:param texture: 纹理图片
:return: 纹理图片的路径
"""
paths = ['./textures', '../textures']
for path in paths:
p = path + "/" + texture
if os.path.exists(p):
return p
return None
def __get_texture_main_color(self, texture):
"""
获取纹理图片的主要颜色
:param texture:
:return:
"""
colors = get_dominant_colors(texture)
first_color = colors[0]
# print(self.name, first_color)
return tuple(np.array(first_color) / 255)
@abstractmethod
def update(self):
"""
更新天体信息和数据,比如:更新天体的位置
:return:
"""
pass
def disappear(self):
"""
天体消失的操作,比如:销毁天体视图对象
:return:
"""
pass
@abstractmethod
def appear(self):
"""
天体显示的操作,比如:构建天体视图对象
:return:
"""
pass
# -*- coding:utf-8 -*-
# title :天体视图
# description :天体视图(天体效果展示用)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from abc import ABCMeta, abstractmethod
from bodies import Body
from common.func import get_dominant_colors
import numpy as np
import os
from common.image_utils import find_texture
class BodyView(metaclass=ABCMeta):
"""
天体视图(天体效果展示用)
"""
def __init__(self, body: Body, bodies_system):
self.body = body
self.bodies_system = bodies_system
self.sphere = None
if self.body.texture is None or self.body.texture == '':
self.color = tuple(np.array(body.color) / 255)
else:
self.texture = self.__find_texture(self.body.texture) # 纹理
if self.texture is None:
self.color = tuple(np.array(body.color) / 255)
else:
self.color = self.__get_texture_main_color(self.texture)
self.appear()
self.position = body.position
self.name = body.name
self.mass = body.mass
self.raduis = body.raduis
self.velocity = body.velocity
self.appeared = True
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.position[0], self.position[1], self.position[2], self.velocity)
def __find_texture(self, texture):
"""
尝试在多个路径下寻找纹理图片
:param texture: 纹理图片
:return: 纹理图片的路径
"""
return find_texture(texture)
# if os.path.exists(texture):
# return texture
# paths = [os.path.join('.', 'textures'), os.path.join('..', 'textures')]
# for path in paths:
# p = os.path.join(path, texture)
# if os.path.exists(p):
# return p
# return ""
def __get_texture_main_color(self, texture):
"""
获取纹理图片的主要颜色
:param texture:
:return:
"""
colors = get_dominant_colors(texture)
first_color = colors[0]
# print(self.name, first_color)
return tuple(np.array(first_color) / 255)
@abstractmethod
def update(self):
"""
更新天体信息和数据,比如:更新天体的位置
:return:
"""
pass
def disappear(self):
"""
天体消失的操作,比如:销毁天体视图对象
:return:
"""
pass
@abstractmethod
def appear(self):
"""
天体显示的操作,比如:构建天体视图对象
:return:
"""
pass
# -*- coding:utf-8 -*-
# title :Mayavi天体视图
# description :Mayavi天体视图(天体效果展示用,需要安装 mayavi)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from mayavi import mlab
from tvtk.api import tvtk
import os
import matplotlib.pyplot as plt
from common.func import get_dominant_colors
from simulators.views.body_view import BodyView
import numpy as np
class MayaviView(BodyView):
"""
Mayavi天体视图(天体效果展示用)
"""
def update(self):
"""
更新天体信息和数据,比如:更新天体的位置
:return:
"""
if hasattr(self.sphere, "mlab_source"):
# self.sphere.mlab_source.x 的位置是已经和 distance_scale 进行了相乘
# body.position 是真实位置,所以需要和 distance_scale 相乘
x_offset = self.body.position[0] * self.body.distance_scale - self.sphere.mlab_source.x
y_offset = self.body.position[1] * self.body.distance_scale - self.sphere.mlab_source.y
z_offset = self.body.position[2] * self.body.distance_scale - self.sphere.mlab_source.z
# self.position 的位置是已经和 distance_scale 进行了相乘
self.sphere.mlab_source.set(x=self.position[0], y=self.position[1], z=self.position[2])
# self.angle += 10
# self.sphere.actor.actor.rotate_z(self.angle)
# self.sphere.actor.actor.rotate_wxyz(self.angle,0,0,0)
if hasattr(self, "rings"):
if hasattr(self.rings, "mlab_source"):
if hasattr(self, "rings") and self.body.has_rings:
x = self.rings.mlab_source.x
y = self.rings.mlab_source.y
z = self.rings.mlab_source.z
x = x + x_offset[0]
y = y + y_offset[0]
z = z + z_offset[0]
self.rings.mlab_source.set(x=x, y=y, z=z)
# return x_offset[0], y_offset[0], z_offset[0]
def build_rings(self):
if not hasattr(self, "rings") or self.rings is None:
R = 2.2
r = 0.5
rings_scale = 0.5e5
resolution = 50
theta = np.linspace(0, 2 * np.pi, resolution)
phi = np.linspace(0, 2 * np.pi, resolution)
torus = np.zeros((3, resolution, resolution))
# # body.position 是真实位置,所以需要和 distance_scale 相乘
for i in range(0, resolution):
for j in range(0, resolution): # size_scale=8.0e2
torus[0][i][j] = (R + r * np.cos(phi[j])) * np.cos(theta[i]) * \
self.body.size_scale * rings_scale + \
self.body.position[0] # * self.body.distance_scale
torus[1][i][j] = (R + r * np.cos(phi[j])) * np.sin(theta[i]) * \
self.body.size_scale * rings_scale + \
self.body.position[1] # * self.body.distance_scale
# 带环的厚度
thicknesses_scale = self.body.raduis * 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)
if hasattr(self.body, "rings_color"):
rings_color = tuple(np.array(self.body.rings_color) / 255)
self.rings = mlab.mesh(torus[0], torus[1], torus[2], color=rings_color,
representation='surface')
return self.rings
def appear(self):
"""
天体显示的操作,比如:构建天体视图对象
:return:
"""
if not hasattr(self, "sphere") or self.sphere is None:
scale_factor = self.body.size_scale * self.body.diameter
sphere = mlab.points3d(self.body.position[0], self.body.position[1], self.body.position[2],
scale_factor=scale_factor,
color=self.color,
resolution=50,
opacity=1,
name=self.body.name)
# # 调整镜面反射参数
sphere.actor.property.specular = 0.5 # 0.1
sphere.actor.property.specular_power = 128
# 设置背面剔除,以更好的显示透明效果
sphere.actor.property.backface_culling = True
# sphere.actor.property.lighting_ = 10
sphere.actor.property.lighting = False
sphere.scene.disable_render = False
# sphere_earth.scene.disable_render = False
sphere.scene.anti_aliasing_frames = 10
# sphere_earth.scene.anti_aliasing_frames = 0
self.sphere = sphere
if hasattr(self, "texture"):
if self.texture is not None and self.texture != '':
self.__set_texture(self.texture)
if self.body.has_rings:
self.build_rings()
# return self.sphere, self.rings
# return self.sphere,
def disappear(self):
if hasattr(self, "sphere"):
self.sphere.visible = False
if hasattr(self, "rings"):
self.rings.visible = False
def __set_texture(self, image_file):
"""
设置纹理图片到天体
:param image_file:
:return:
"""
outfile = image_file.replace('.jpg', '_flipped.jpg')
if os.path.exists(outfile):
image_file = outfile
else:
if not os.path.exists(image_file):
return
img = plt.imread(image_file)
img = img[::-1, ...]
plt.imsave(outfile, img)
image_file = outfile
img = tvtk.JPEGReader(file_name=image_file)
texture = tvtk.Texture(input_connection=img.output_port, interpolate=0, repeat=0)
self.sphere.actor.actor.texture = texture
self.sphere.actor.tcoord_generator_mode = 'sphere'
cylinder_mapper = self.sphere.actor.tcoord_generator
cylinder_mapper.prevent_seam = 0
# -*- coding:utf-8 -*-
# title :Mayavi天体视图
# description :Mayavi天体视图(天体效果展示用,需要安装 mayavi)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
from mayavi import mlab
from tvtk.api import tvtk
import os
import matplotlib.pyplot as plt
from common.func import get_dominant_colors
from simulators.views.body_view import BodyView
import numpy as np
class MayaviView(BodyView):
"""
Mayavi天体视图(天体效果展示用)
"""
def update(self):
"""
更新天体信息和数据,比如:更新天体的位置
:return:
"""
if hasattr(self.sphere, "mlab_source"):
# self.sphere.mlab_source.x 的位置是已经和 distance_scale 进行了相乘
# body.position 是真实位置,所以需要和 distance_scale 相乘
x_offset = self.body.position[0] * self.body.distance_scale - self.sphere.mlab_source.x
y_offset = self.body.position[1] * self.body.distance_scale - self.sphere.mlab_source.y
z_offset = self.body.position[2] * self.body.distance_scale - self.sphere.mlab_source.z
# self.position 的位置是已经和 distance_scale 进行了相乘
self.sphere.mlab_source.set(x=self.position[0], y=self.position[1], z=self.position[2])
if hasattr(self, "rings"):
if hasattr(self.rings, "mlab_source"):
if hasattr(self, "rings") and self.body.has_rings:
x = self.rings.mlab_source.x
y = self.rings.mlab_source.y
z = self.rings.mlab_source.z
x = x + x_offset[0]
y = y + y_offset[0]
z = z + z_offset[0]
self.rings.mlab_source.set(x=x, y=y, z=z)
# return x_offset[0], y_offset[0], z_offset[0]
def build_rings(self):
if not hasattr(self, "rings") or self.rings is None:
R = 2.2
r = 0.5
rings_scale = 0.5e5
resolution = 50
theta = np.linspace(0, 2 * np.pi, resolution)
phi = np.linspace(0, 2 * np.pi, resolution)
torus = np.zeros((3, resolution, resolution))
# # body.position 是真实位置,所以需要和 distance_scale 相乘
for i in range(0, resolution):
for j in range(0, resolution): # size_scale=8.0e2
torus[0][i][j] = (R + r * np.cos(phi[j])) * np.cos(theta[i]) * \
self.body.size_scale * rings_scale + \
self.body.position[0] # * self.body.distance_scale
torus[1][i][j] = (R + r * np.cos(phi[j])) * np.sin(theta[i]) * \
self.body.size_scale * rings_scale + \
self.body.position[1] # * self.body.distance_scale
# 带环的厚度
thicknesses_scale = self.body.raduis * 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)
if hasattr(self.body, "rings_color"):
rings_color = tuple(np.array(self.body.rings_color) / 255)
self.rings = mlab.mesh(torus[0], torus[1], torus[2], color=rings_color,
representation='surface')
return self.rings
def appear(self):
"""
天体显示的操作,比如:构建天体视图对象
:return:
"""
if hasattr(self.body, "torus_stars"):
# 暂不支持环状小行星群
return
if not hasattr(self, "sphere") or self.sphere is None:
scale_factor = self.body.size_scale * self.body.diameter
sphere = mlab.points3d(self.body.position[0], self.body.position[1], self.body.position[2],
scale_factor=scale_factor,
color=self.color,
resolution=50,
opacity=1,
name=self.body.name)
# # 调整镜面反射参数
sphere.actor.property.specular = 0.5 # 0.1
sphere.actor.property.specular_power = 128
# 设置背面剔除,以更好的显示透明效果
sphere.actor.property.backface_culling = True
# sphere.actor.property.lighting_ = 10
sphere.actor.property.lighting = False
sphere.scene.disable_render = False
# sphere_earth.scene.disable_render = False
sphere.scene.anti_aliasing_frames = 10
# sphere_earth.scene.anti_aliasing_frames = 0
self.sphere = sphere
if hasattr(self, "texture"):
if self.texture is not None and self.texture != '':
self.__set_texture(self.texture)
if self.body.has_rings:
self.build_rings()
# return self.sphere, self.rings
# return self.sphere,
def disappear(self):
if hasattr(self, "sphere"):
self.sphere.visible = False
if hasattr(self, "rings"):
self.rings.visible = False
def __set_texture(self, image_file):
"""
设置纹理图片到天体
:param image_file:
:return:
"""
outfile = image_file.replace('.jpg', '_flipped.jpg').replace('.png', '_flipped.jpg')
if os.path.exists(outfile):
image_file = outfile
else:
if not os.path.exists(image_file):
return
img = plt.imread(image_file)
img = img[::-1, ...]
plt.imsave(outfile, img)
image_file = outfile
img = tvtk.JPEGReader(file_name=image_file)
texture = tvtk.Texture(input_connection=img.output_port, interpolate=0, repeat=0)
self.sphere.actor.actor.texture = texture
self.sphere.actor.tcoord_generator_mode = 'sphere'
cylinder_mapper = self.sphere.actor.tcoord_generator
cylinder_mapper.prevent_seam = 0
# -*- coding:utf-8 -*-
# title :matplotlib天体视图
# description :matplotlib天体视图(天体效果展示用)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
import os
import matplotlib.pyplot as plt
from common.func import get_dominant_colors
from simulators.views.body_view import BodyView
import numpy as np
class MplView(BodyView):
"""
matplotlib天体视图(天体效果展示用)
"""
def update(self):
pass
def appear(self):
if hasattr(self.body, "torus_stars"):
# 暂不支持环状小行星群
return
def disappear(self):
pass
# -*- coding:utf-8 -*-
# title :ursina天体视图
# description :ursina天体视图(天体效果展示用,需要安装 ursina)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
# pip install -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com ursina
from ursina import Ursina, window, Entity, Mesh, EditorCamera, color, mouse, Vec2, Vec3, load_texture, Texture
from math import pi, sin, cos
import numpy as np
import math
def create_sphere(radius, subdivisions):
"""
创建一个球体
:param radius:
:param subdivisions:
:return:
"""
# 生成球体的顶点、UV坐标uvs、法线tris和三角面
verts = []
tris = []
normals = []
uvs = []
for y in range(subdivisions + 1):
for x in range(subdivisions + 1):
x_segment = x / subdivisions
y_segment = -y / subdivisions
x_pos = cos(x_segment * 2 * pi) * sin(y_segment * pi)
y_pos = cos(y_segment * pi)
z_pos = sin(x_segment * 2 * pi) * sin(y_segment * pi)
verts.append(Vec3(x_pos, y_pos, z_pos) * radius)
uvs.append(Vec2(x_segment, y_segment))
normals.append(Vec3(x_pos, y_pos, z_pos))
for y in range(subdivisions):
for x in range(subdivisions):
first = (y * (subdivisions + 1)) + x
second = first + subdivisions + 1
# tris.append((first, second + 1, second))
# tris.append((first, first + 1, second + 1))
tris.append((second, second + 1, first))
tris.append((second + 1, first + 1, first))
# 反转面法线
# for i in range(len(tris)):
# a, b, c = tris[i]
# tris[i] = (c, b, a)
# normals[a], normals[b], normals[c] = -Vec3(*normals[c]), -Vec3(*normals[b]), -Vec3(*normals[a])
# normals[a], normals[b], normals[c] = -Vec3(*normals[a]), -Vec3(*normals[b]), -Vec3(*normals[c])
# 翻转球体
# for i in range(len(normals)):
# normals[i] = -normals[i]
return Mesh(vertices=verts, triangles=tris, normals=normals, uvs=uvs, mode='triangle')
def create_body_torus(inner_radius, outer_radius, subdivisions):
vertices = []
uvs = []
normals = []
triangles = []
# 计算圆环顶点、法向量和纹理坐标
for i in range(subdivisions):
for j in range(subdivisions):
# 计算纹理坐标
u = i / subdivisions
v = j / subdivisions
# 计算球面坐标系下的角度
theta = u * math.pi * 2
phi = v * math.pi * 2
# 计算圆环顶点位置
x = (outer_radius + inner_radius * math.cos(phi)) * math.cos(theta)
y = inner_radius * math.sin(phi) * (inner_radius) / 2
z = (outer_radius + inner_radius * math.cos(phi)) * math.sin(theta)
# 计算圆环顶点法向量
nx = math.cos(theta) * math.cos(phi)
ny = math.sin(phi)
nz = math.sin(theta) * math.cos(phi)
vertices.append((x, y, z))
normals.append((nx, ny, nz))
uvs.append((u, v))
# 计算圆环三角形面片
for i in range(subdivisions):
for j in range(subdivisions):
i1 = i
j1 = j
i2 = (i + 1) % subdivisions
j2 = (j + 1) % subdivisions
p1 = i1 * subdivisions + j1
p2 = i2 * subdivisions + j1
p3 = i2 * subdivisions + j2
p4 = i1 * subdivisions + j2
triangles.append((p1, p2, p3))
triangles.append((p1, p3, p4))
# uvs = [[u * 2, v] for u, v in uvs]
# 创建 mesh 对象
mesh = Mesh(vertices=vertices, uvs=uvs, normals=normals, triangles=triangles, mode='triangle')
return mesh
def create_torus(inner_radius, outer_radius, subdivisions, repeat=1):
verts = []
tris = []
uvs = []
for i in range(subdivisions):
angle = i * (360 / subdivisions)
x = np.cos(angle * np.pi / 180)
y = np.sin(angle * np.pi / 180)
# create vertices for inner radius
inner_x = x * inner_radius
inner_y = y * inner_radius
# create vertices for outer radius
outer_x = x * outer_radius
outer_y = y * outer_radius
if i % int(subdivisions / repeat) == 0:
verts.append((inner_x, inner_y, 0))
verts.append((outer_x, outer_y, 0))
uvs.append((0.999, 0.0))
uvs.append((0.999, 0.999))
verts.append((inner_x, inner_y, 0))
verts.append((outer_x, outer_y, 0))
uvs.append((0.001, 0.0))
uvs.append((0.001, 0.999))
else:
verts.append((inner_x, inner_y, 0))
verts.append((outer_x, outer_y, 0))
# create uvs
u = angle * repeat / 360 % 1
uvs.append((u, 0.0))
uvs.append((u, 0.999))
# create triangles
first_index = i * 2
second_index = (i * 2 + 2) % (subdivisions * 2)
third_index = (i * 2 + 1) % (subdivisions * 2)
fourth_index = (i * 2 + 3) % (subdivisions * 2)
tris.append((first_index, second_index, third_index))
tris.append((third_index, second_index, fourth_index))
# create normals
normals = []
for i in range(len(verts)):
angle = i * (360 / subdivisions)
x = np.cos(angle * np.pi / 180)
y = np.sin(angle * np.pi / 180)
normals.append((x, y, 0))
# create mesh
mesh = Mesh(vertices=verts, triangles=tris, uvs=uvs, normals=normals, mode='triangle')
# add color attribute
# mesh.colorize()
return mesh
if __name__ == '__main__':
app = Ursina()
# # 使用 Mesh 类创建球体
texture = "../../textures/saturn.jpg"
textureRings = '../../textures/saturnRings.jpg'
textureAsteroids = '../../textures/asteroids.png'
# 创建球体
# sphere = create_sphere(1, 32)
# entity = Entity(model=sphere, texture=texture, color=color.white)
# 创建光晕
# glow_entity = Entity(parent=entity, model='sphere', color=color.rgb(1,1,1,0.1),
# scale=2.1, alpha=0.1)
#
# torus = create_body_torus(0.8, 2, 64)
# textureRings = load_texture(textureRings)
# entity = Entity(model=torus, texture=textureRings, rotation=(0, 0, 0), double_sided=True)
# torus = create_torus(1.5, 3, 64)
# entity = Entity(model=torus, texture=textureRings, rotation=(85, 0, 0), double_sided=True)
body_torus = create_torus(9, 10, 64)
entities = Entity(model=body_torus, texture=textureAsteroids, rotation=(85, 0, 0), double_sided=True)
entities.set_light_off()
EditorCamera()
app.run()
# -*- coding:utf-8 -*-
# title :ursina天体视图
# description :ursina天体视图(天体效果展示用,需要安装 ursina)
# author :Python超人
# date :2023-02-11
# link :https://gitcode.net/pythoncr/
# python_version :3.8
# ==============================================================================
# pip install -i http://pypi.douban.com/simple/ --trusted-host=pypi.douban.com ursina
from ursina import Ursina, window, Entity, Mesh, SmoothFollow, Texture, clamp, time, \
camera, color, mouse, Vec2, Vec3, Vec4, Text, \
load_texture, held_keys, destroy, PointLight
from ursina.prefabs.first_person_controller import FirstPersonController
import sys
from bodies import Body
from simulators.ursina.ursina_config import UrsinaConfig
from simulators.ursina.ursina_event import UrsinaEvent
from common.color_utils import adjust_brightness, conv_to_vec4_color, get_inverse_color
from simulators.views.body_view import BodyView
from simulators.views.ursina_mesh import create_sphere, create_torus
import numpy as np
import math
class UrsinaPlayer(FirstPersonController):
"""
"""
# body_rotation_speed_control = 1.0
def __init__(self, position, view_azimuth=0, targets=None):
super().__init__()
# camera.fov = 2000 # 100
# camera.rotation_y = 90
self.planets = None
if targets is not None:
self.planets = []
# targets = [view.planet.parent for view in targets]
# targets_parent = Entity()
for view in targets:
# view.planet.parent = targets_parent
self.planets.append(view.planet)
# self.camera_adj(planets)
# # planets.append(view.planet)
#
# camera.add_script(SmoothFollow(targets_parent, offset=(0, 8, -20)))
pos = np.array(position) * UrsinaConfig.SCALE_FACTOR
self.position = Vec3(pos[0], pos[1], pos[2])
# 将摄像机位置设置为 x=0、y=1、z=0 的位置
camera.position = Vec3(pos[0], pos[1], pos[2])
# self.x = 90
# self.position = Vec3(pos[0], pos[1], pos[2])
# 将摄像机的观察角度绕 x 轴旋转 45 度,绕 y 轴旋转 0 度,绕 z 轴旋转 0 度
# self.rotation = Vec3(45, 90, 0)
# camera.look_at(Vec3(0, 0, 0))
# camera.world_rotation = Vec3(0, 190, 190)
# camera.enabled = True
# self.gravity = 0
# self.vspeed = 400
# self.speed = 1000
# self.mouse_sensitivity = Vec2(160, 160)
# self.on_enable()
# self.rotation_speed = 80
self.on_disable() # 防止鼠标被窗口锁定
# def input(self, key):
# if key == "escape":
# if mouse.locked:
# self.on_disable()
# else:
# sys.exit()
# return super().input(key)
class Planet(Entity):
def on_reset(self):
# 删除拖尾
self.clear_trails()
self.body_view.body.reset()
def __init__(self, body_view: BodyView):
self.body_view = body_view
self.rotation_speed = self.body_view.body.rotation_speed
self.rotMode = 'x' # random.choice(["x", "y", "z"])
self.name = body_view.name
pos = body_view.position * body_view.body.distance_scale * UrsinaConfig.SCALE_FACTOR
scale = body_view.body.diameter * body_view.body.size_scale * UrsinaConfig.SCALE_FACTOR
self.init_scale = scale
if hasattr(body_view, "texture"):
texture = load_texture(body_view.texture)
else:
texture = None
if hasattr(self.body_view.body, "torus_stars"):
# 创建一个星环小天体群(主要模拟小行星群,非一个天体)
model = create_torus(0.83, 1.05, 64, 1)
rotation = (90, 0, 0)
else:
# 创建一个天体
model = create_sphere(0.5, 32)
rotation = (0, 0, 0)
UrsinaEvent.on_reset_subscription(self.on_reset)
# color.white
self.plant_color = color.white
# self.plant_color = color.rgba(*self.body_view.color)
super().__init__(
# model="sphere",
model=model,
scale=scale,
texture=texture,
color=self.plant_color,
position=pos,
rotation=rotation,
double_sided=True
)
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:
# 一个天体
# 拖尾球体的初始化
self.trail_init()
if self.body_view.body.is_fixed_star:
self.create_fixed_star_lights()
if self.body_view.body.show_name:
self.create_name_text()
def create_name_text(self):
b_color = self.body_view.color
self.name_text = Text(self.body_view.body.name, scale=1, billboard=True, parent=self,
font=UrsinaConfig.CN_FONT, background=True,
origin=(0, 0))
self.name_text.background.color = color.rgba(b_color[0], b_color[1], b_color[2], 0.3)
# self.name_text.scale = self.scale
inverse_color = get_inverse_color(b_color)
self.name_text.color = color.rgba(inverse_color[0], inverse_color[1], inverse_color[2], 1)
def trail_init(self):
"""
拖尾球体的初始化
:return:
"""
# 存放拖尾球体
self.trails = {}
# 根据天体的颜色获取拖尾的颜色
trail_color = conv_to_vec4_color(self.body_view.body.trail_color)
trail_color = adjust_brightness(trail_color, 0.4)
self.trail_color = color.rgba(trail_color[0], trail_color[1], trail_color[2], 0.6)
# 拖尾球体的大小为该天体的 1/5
self.trail_scale = self.scale_x / 5
if self.trail_scale < 1:
# 如果太小,则
pass
def distance_between_two_points(self, point_a: Vec3, point_b: Vec3) -> float:
# 计算两点在 x、y、z 三个坐标轴上的差值
diff_x = point_a.x - point_b.x
diff_y = point_a.y - point_b.y
diff_z = point_a.z - point_b.z
# 计算两点之间的距离
distance = math.sqrt(diff_x ** 2 + diff_y ** 2 + diff_z ** 2)
return distance
def create_trails(self):
"""
创建拖尾
:return:
"""
# 当前天体的位置
try:
pos = self.position
except Exception as e:
print(self.body_view.body)
self.destroy_all()
return
trails_keys = self.trails.keys()
# 如果有拖尾
if len(trails_keys) > 0:
# 获取最后一个拖尾的位置
last_key = list(trails_keys)[-1]
last_pos = self.trails[last_key]
# 获取拖尾与当前天体的位置
last_pos_distance = self.distance_between_two_points(pos, last_pos)
self_pos_distance = self.distance_between_two_points(pos, self.position)
# # 如果拖尾在天体的内部也不要生成
# if self_pos_distance < self.scale_x + (self.trail_scale / 2):
# pass
# 如果位置比较近,就不创建拖尾了,保证拖尾间隔一定的距离
if last_pos_distance < self.trail_scale * 1.2: # 间隔距离不小于1.2倍的拖尾球体
return
# 创建拖尾球体,并作为字典的key,存放拖尾球体的位置
self.trails[self.create_trail(pos)] = pos
# 计算拖尾球体超过的数量
trail_overflow_count = len(self.trails) - UrsinaConfig.trail_length
if trail_overflow_count > 0:
# 如果拖尾球体超过的数量,就删除之前的拖尾球体
for entity, pos in self.trails.items():
destroy(entity)
trail_overflow_count -= 1
if trail_overflow_count <= 0:
break
def create_trail(self, pos):
"""
在天体当前的位置创建一个拖尾球体
:param pos:
:return:
"""
# sphere = create_sphere(1,6) diamond sphere
trail = Entity(model='sphere', color=self.trail_color, scale=self.trail_scale, position=pos)
trail.set_light_off()
# trail.set_color_off()
# trail.set_color_scale_off()
# trail.enabled = False
return trail
def turn(self):
if hasattr(self.body_view.body, "torus_stars"):
# 星环小天体群(主要模拟小行星群,非一个天体)不受 body_size_factor 影响
self.scale = self.init_scale
else:
self.scale = self.init_scale * UrsinaConfig.body_size_factor
pos = self.body_view.position * UrsinaConfig.SCALE_FACTOR
if self.body_view.body.parent is None:
self.x = -pos[1]
self.y = pos[2]
self.z = pos[0]
else:
self.follow_parent()
dt = 0
if hasattr(self.body_view.body, "dt"):
dt = self.body_view.body.dt
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)
else:
# 是通过月球保持一面面对地球,调整得到
self.rotspeed = self.rotation_speed * (dt / 3600) / 2.4 * \
UrsinaConfig.ROTATION_SPEED_FACTOR * UrsinaConfig.body_spin_factor
# rotation_speed 度/小时 dt 秒 = (dt / 3600)小时
# if self.rotation_y < 0:
# self.rotation_y += 360
try:
# 天体旋转
self.rotation_y -= self.rotspeed
except Exception as e:
print(self.body_view.body)
self.destroy_all()
return
# 如果有行星环
if hasattr(self, "ring"):
# 如果有行星环,则不让行星环跟随行星转动
self.ring.rotation = -Vec3(self.rotation_x - self.ring_rotation_x,
self.rotation_y,
self.rotation_z)
if UrsinaConfig.show_trail:
# 有时候第一个位置不正确,所以判断一下有历史记录后在创建
if len(self.body_view.body.his_position()) > 1:
self.create_trails()
else:
self.clear_trails()
if hasattr(self, "name_text"):
d = (camera.world_position - self.name_text.world_position).length()
if d < pow(self.scale_x, 1.02) * 1.2:
self.name_text.visible = False
else:
self.name_text.visible = True
# print(d, self.name_text.text, self.scale_x ,self.scale_x*1.23)
# # 计算相机和实体之间的距离
# distance = (camera.world_position - self.world_position).length()
# # 根据距离设置文本缩放比例
# self.name_text.scale = distance / 10
def follow_parent(self):
if not hasattr(self, "f_parent"):
if not hasattr(self.body_view, "bodies_system"):
return
sys = self.body_view.bodies_system
for b in sys.bodies:
if self.body_view.body.parent == b:
self.f_parent = b
break
pos = self.f_parent.position * UrsinaConfig.SCALE_FACTOR
self.x = -pos[1]
self.y = pos[2]
self.z = pos[0]
def create_fixed_star_lights(self):
"""
创建恒星的发光的效果、并作为灯光源
:param entity:
:return:
"""
# 如果是恒星(如:太阳),自身会发光,则需要关闭灯光
self.set_light_off()
# lights = []
# # 创建多个新的 Entity 对象,作为光晕的容器
# _color = color.rgba(1.0, 0.6, 0.2, 1)
if hasattr(self.body_view.body, "glows"):
# glows = (glow_num:10, glow_scale:1.03 glow_alpha:0.1~1)
glows = self.body_view.body.glows
if glows is not None:
if isinstance(glows, tuple):
if len(glows) == 3:
glow_num, glow_scale, glow_alpha = glows
elif len(glows) == 2:
glow_num, glow_scale = glows
glow_alpha = None
else:
glow_num = glows
glow_scale = 1.02
glow_alpha = None
if glow_num > 0:
glow_alphas = [0, 0.5, 0.4, 0.3, 0.2, 0.1]
if glow_alpha is None:
if glow_num < len(glow_alphas) - 1:
glow_alpha = glow_alphas[glow_num]
else:
glow_alpha = glow_alphas[-1]
# _color = color.white
_color = self.body_view.body.color
_color = color.rgba(_color[0] / 255, _color[1] / 255, _color[2] / 255, 1)
for i in range(glow_num):
glow_entity = Entity(parent=self, model='sphere', color=_color,
scale=math.pow(glow_scale, i + 1), alpha=glow_alpha)
if hasattr(self.body_view.body, "light_on"):
if self.body_view.body.light_on:
for i in range(2):
# 创建 PointLight 对象,作为恒星的灯光源
light = PointLight(parent=self, intensity=10, range=10, color=color.white)
def create_rings(self):
"""
创建行星环(使用土星贴图)
:return:
"""
# 行星环偏移角度
# self.ring_rotation_x = 80
# 创建行星环
# self.ring = Entity(parent=self.planet, model='circle', texture='../textures/saturnRings.jpg', scale=3.5,
# rotation=(self.ring_rotation_x, 0, 0), double_sided=True)
# 行星环偏移角度
self.ring_rotation_x = 80
# 创建行星环
torus = create_torus(0.7, 1.2, 64)
self.ring = Entity(parent=self, model=torus, texture='../textures/saturnRings.jpg', scale=1,
rotation=(self.ring_rotation_x, 0, 0), double_sided=True)
# 设置行星环不受灯光影响,否则看不清行星环
self.ring.set_light_off()
def clear_trails(self):
if not hasattr(self, "trails"):
return
# 删除拖尾
for entity, pos in self.trails.items():
destroy(entity)
self.trails.clear()
def destroy_all(self):
# 从天体系统中移除自己(TODO:暂时还不能移除)
# self.body_view.bodies_system.bodies.remove(self.body_view.body)
# 删除拖尾
self.clear_trails()
# 如果有行星环,则删除行星环
if hasattr(self, "ring"):
destroy(self.ring)
self.body_view.body.appeared = False
self.body_view.appeared = False
# 最后删除自己
destroy(self)
class UrsinaView(BodyView):
"""
ursina天体视图(天体效果展示用)
"""
def __init__(self, body: Body, bodies_system):
BodyView.__init__(self, body, bodies_system)
self.velocity = body.velocity
self.planet = Planet(self)
if body.has_rings:
# 创建行星环(目前只有土星环)
self.planet.create_rings()
def update(self):
"""
:return:
"""
self.planet.turn()
def appear(self):
pass
def disappear(self):
self.planet.destroy_all()
self.appeared = False
因为 它太大了无法显示 image diff 。你可以改为 查看blob
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册