Как часто хочется себе представить как движения планет зависят от звезды? Сегодня для этого не нужен телескоп! Представить движения планет в зависимости от объекта очень большой массы можно и на компьютере.
Идея для данной программы зародилась в момент, когда я захотел наглядно представить себе то, как движутся планеты нашей солнечной системы, и как бы менялась траектория движения планет относительно солнца при различных начальных условиях.
Долго тянуть не буду и сразу перейду к краткому описанию программы. Решил писать на питоне. Суть заключается в том, что мы можем создавать планеты в любой точке плоскости, с любым радиусом, массой, цветом и начальной скоростью и моделировать их взаимодействие.Чтобы было не очень сумбурно, решил в куски с кодом вставлять комментарии.
Сначала продемонстрирую пример работы: создаю Солнце и 6 планет, левым придаю скорость вверх, а правым - вниз, равную по модулю. Несмотря на то, что в начальный момент скорость каждой из планет одинакова, свой полный оборот вокруг Солнца они завершают в разное время. Это происходит из-за разницы в начальных расстояниях.
Первый блок: просто импортируем необходимые библиотеки
import pygame
import time
Второй блок: создаём необходимые переменные
fps = 120 # просто fps
G = 6.67 / 10 ** 11 # сохраняем константу гравитационной постоянной
k = 1000000 # метров в одном пикселе
mouse_x, mouse_y = 0, 0 # начальное положение мышки (начальные значения не очень влияют)
time_speed = 10000 # коэффициент ускорения программы
# При коэффициенте ускорения больше 10 ^ 5, появляется очень большая погрешность
Третий блок: запускаем окно, задаём ширину, высоту и переменные для отображения fps и ускорения
pygame.init()
screen = pygame.display.set_mode((1500, 800))
pygame.display.set_caption("Planetary acceleration")
style = pygame.font.SysFont("arial", 36)
render_fps = style.render('fps ' + str(fps), True, 'blue')
render_time_speed = style.render('acceleration: ' + str(time_speed), True, 'blue')
Четвёртый блок: для удобства создадим класс, который будет отвечать за одну планету
class Planet:
def __init__(self, x, y, r, m, color): # задаём начальные параметры
self.x = x # координата по оси Ох
self.y = y # координата по оси Оу
self.r = r # радиус планеты
self.m = m # масса планеты
self.color = color # цвет планеты
self.speed = [0, 0] # скорость которую имеет планета
self.f = [0, 0] # сила, которые действуют на планету
self.status = True # существует планета или уже нет
self.trace_count = 0 # переменная, чтобы "не очень часто" выводить траекторию планеты, иначе сильно упало бы fps
self.trace = [] # точки траектории нашей планеты
def update_coordinates(self): # обновляем скорость, координату и траекторию
self.speed[0] += (self.f[0] / self.m) * time_speed ** 2 / fps ** 2
self.speed[1] += (self.f[1] / self.m) * time_speed ** 2 / fps ** 2
self.x += self.speed[0]
self.y += self.speed[1]
self.trace_count += (self.speed[0] ** 2 + self.speed[1] ** 2) ** 0.5
if self.trace_count / k >= 7: # чем меньше число после знака, тем более сплошной будет траектория при выводе
self.trace_count = 0
self.trace.append((self.x, self.y))
if len(self.trace) > 1000: # Также, чтобы не проседало fps, когда много точек, начинаем удалять с конца
self.trace.pop(0)
def draw(self): # отображаем все планеты и их траектории
pygame.draw.circle(screen, self.color, ((self.x - mouse_x) / k, (self.y - mouse_y) / k), self.r / k)
for i in self.trace:
pygame.draw.circle(screen, self.color, ((i[0] - mouse_x) / k, (i[1] - mouse_y) / k), 1)
Пятый блок: нужен для определения всех сил, действующих на каждую планету
def update_forces(planets, collides):
for i in range(len(planets)):
for j in range(i + 1, len(planets)):
dif_x = planets[j].x - planets[i].x # разница между двумя планетами по х
dif_y = planets[j].y - planets[i].y # разница между двумя планетами по y
d = (dif_x ** 2 + dif_y ** 2) ** 0.5 # пифагорово расстояние между планетами
f = G * planets[i].m * planets[j].m / d ** 2 # Сила между двумя планетами
planets[i].f[0] += dif_x * f / d # обновляем действующие силы на тело
planets[i].f[1] += dif_y * f / d
planets[j].f[0] -= dif_x * f / d
planets[j].f[1] -= dif_y * f / d
if planets[i].r + planets[j].r > d: # Находим какие две планеты сталкиваются друг с другом
collides.append((i, j))
Шестой блок: обрабатываем все столкновения планет
def remove_collides(planets, collides):
for i in collides:
p1 = planets[i[0]]
p2 = planets[i[1]]
if p1.status and p2.status: # если обе планеты ещё существуют
if p1.m > p2.m: # какая планета больше по массе, та и выиграла
new_planet = Planet(p1.x, p1.y, p1.r + p2.r, p1.m + p2.m, p1.color)
else:
new_planet = Planet(p2.x, p2.y, p1.r + p2.r, p1.m + p2.m, p2.color)
new_planet.speed = [(p1.m * p1.speed[0] + p2.m * p2.speed[0]) / (p1.m + p2.m),
(p1.m * p1.speed[1] + p2.m * p2.speed[1]) / (p1.m + p2.m)]
planets.append(new_planet)
p1.status = p2.status = 0
Седьмой блок: для примера, создаём солнце и несколько планет
planets = []
# Солнце
p = Planet(0, 0, 696_000_000, 1.9891 * 10 ** 30, 'orange')
planets.append(p)
for i in range(14, 20):
p = Planet((-1) ** i * (i * 10 ** 9 + 10 ** 9), 0, 1, 1, 'green')
planets.append(p)
planets[i - 13].speed[1] += (-1) ** i * 29273.6 * time_speed / fps
Восьмой блок: запускаем основную программу, обрабатываем все команды, которые могут поступать от пользователя и обновляем массив, в котором содержаться ещё "живые" планеты
tick = 0
tm = time.time()
running = True # стандартная переменная для запуска цикла
while running:
tick += 1
if tick == 100: # чтобы fps в левом углу не сходил с ума и не происходило деление на ноль
tick = 0
render_fps = style.render("fps:" + str(int(100 / (time.time() - tm))), True, "blue")
tm = time.time()
for event in pygame.event.get(): # начинаем обрабатывать команды
if event.type == pygame.QUIT: # выход
running = False
if event.type == pygame.MOUSEBUTTONDOWN: # нажатие любой кнопки мыши
x = event.pos[0] # определяем где находится мышка у пользователя
y = event.pos[1]
new_x = mouse_x + x * k # считаем коэффициенты для в нашей "размерности"
new_y = mouse_y + y * k
if event.button == 4: # колёсико мышки вперёд - приближение космической карты
k *= 0.85
mouse_x = new_x - x * k
mouse_y = new_y - y * k
if event.button == 5: # колёсико мышки назад - отдаление космической карты
k /= 0.85
mouse_x = new_x - x * k
mouse_y = new_y - y * k
if event.button == 3: # правая кнопка мыши - добавляем планету в точку, куда указывает мышка
planets.append(Planet(new_x, new_y, 6371000, 5.9722 * 10 ** 24, 'blue'))
if event.button == 2: # нажатие колёсика - ускорение работы программы
if time_speed >= 100000:
time_speed = 1
for i in planets:
i.speed[0] /= 100000 # уменьшение скоростей планет
i.speed[1] /= 100000
else:
time_speed *= 10
for i in planets:
i.speed[0] *= 10
i.speed[1] *= 10
render_time_speed = style.render('acceleration: ' + str(time_speed), True, 'blue') # обновляем отображение ускорения
if event.type == pygame.MOUSEMOTION: # для перемещения по космической карте с помощью удержания левой кнопки мыши
if pygame.mouse.get_pressed()[0]:
mouse_x -= event.rel[0] * k
mouse_y -= event.rel[1] * k
collides = [] # массив для определения кортежей столкновений
update_forces(planets, collides) # находим все столкновения
remove_collides(planets, collides) # обрабатываем все столкновения
screen.fill("black") # обновляем чёрный фон
new_planets = []
for planet in planets:
if planet.status: # выбираем только те планеты, которые ещё не столкнулись
planet.update_coordinates() # обновляем их значения
planet.f = [0, 0] # сбрасываем действующие силы
new_planets.append(planet) # сохраняем "живые" планеты
planet.draw() # отображаем всё
planets = new_planets # обновляем массив
screen.blit(render_fps, (10, 10)) # выводим fps
screen.blit(render_time_speed, (10, 50)) # выводим ускорение
pygame.display.update()
pygame.quit() # заканчиваем
Всё. Это вся программа, которая необходима для данной задачи.
Под конец, давайте полностью смоделируем работу Солнечной системы!
Добавим код для создания Солнца и всех планет
# Земля:
p = Planet(-152 * 10 ** 9, 0, 6371000, 5.9722 * 10 ** 24, 'green')
planets.append(p)
planets[0].speed[1] += 29273.6 * time_speed / fps # скорость вокруг солнца
# Луна:
p = Planet(-363104000 - 152 * 10 ** 9, 0, 1737100, 7.35 * 10 ** 22, 'grey')
planets.append(p)
planets[1].speed[1] = 1023 * time_speed / fps # скорость вокруг земли
planets[1].speed[1] += 29273.6 * time_speed / fps # скорость вокруг солнца
# Меркурий
p = Planet(-69.8 * 10 ** 9, 0, 2439700, 3.33022 * 10 ** 23, "yellow")
planets.append(p)
planets[2].speed[1] += 38000 * time_speed / fps # Скорость вокруг солнца
# Венера
p = Planet(-109 * 10 ** 9, 0, 6051800, 4.8675 * 10 ** 24, "white")
planets.append(p)
planets[3].speed[1] += 34000 * time_speed / fps
# Марс
p = Planet(-249.2 * 10 ** 9, 0, 3390000, 6.4171 * 10 ** 23, "red")
planets.append(p)
planets[4].speed[1] += 23000 * time_speed / fps
# Юпитер
p = Planet(-816.521 * 10 ** 9, 0, 71492000, 1.8986 * 10 ** 27, "brown")
planets.append(p)
planets[5].speed[1] += 12500 * time_speed / fps
# Сатурн
p = Planet(-1.51 * 10 ** 12, 0, 60268000, 5.6846 * 10 ** 26, "beige")
planets.append(p)
planets[6].speed[1] += 9100 * time_speed / fps
# Уран
p = Planet(-3 * 10 ** 12, 0, 25362000, 8.6813 * 10 ** 25, "pink")
planets.append(p)
planets[7].speed[1] += 6300 * time_speed / fps
# Нептун
p = Planet(-4.5 * 10 ** 12, 0, 24622000, 1.02409 * 10 ** 26, "blue")
planets.append(p)
planets[8].speed[1] += 4950 * time_speed / fps
# Солнце
p = Planet(0, 0, 696_000_000, 1.9891 * 10 ** 30, 'orange')
planets.append(p)
В связи с тем, что расстояние между планетами очень большое, сначала я решил показать симуляцию движения планет земного типа
Теперь немного отдалимся от Солнца и посмотрим на оставшиеся планеты
Это всё, что я хотел рассказать и показать вам сегодня. Смело копируйте программу и проводите свои собственные эксперименты на космической карте!