Как часто хочется себе представить как движения планет зависят от звезды? Сегодня для этого не нужен телескоп! Представить движения планет в зависимости от объекта очень большой массы можно и на компьютере.
Идея для данной программы зародилась в момент, когда я захотел наглядно представить себе то, как движутся планеты нашей солнечной системы, и как бы менялась траектория движения планет относительно солнца при различных начальных условиях.
Долго тянуть не буду и сразу перейду к краткому описанию программы. Решил писать на питоне. Суть заключается в том, что мы можем создавать планеты в любой точке плоскости, с любым радиусом, массой, цветом и начальной скоростью и моделировать их взаимодействие.Чтобы было не очень сумбурно, решил в куски с кодом вставлять комментарии.
Сначала продемонстрирую пример работы: создаю Солнце и 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)
В связи с тем, что расстояние между планетами очень большое, сначала я решил показать симуляцию движения планет земного типа

Теперь немного отдалимся от Солнца и посмотрим на оставшиеся планеты

Это всё, что я хотел рассказать и показать вам сегодня. Смело копируйте программу и проводите свои собственные эксперименты на космической карте!
