Leo Matyushkin 14 апреля 2021

🎞️ Как с помощью Python делать красивые математические анимации

Показываем на примерах, как использовать навыки Python, чтобы сделать красивые математические анимации в духе YouTube-канала 3Blue1Brown.

3Blue1Brown – популярный математический YouTube-канал Гранта Сандерсона. Мы и сами любим 3Blue1Brown за отличные объяснения и классные анимации. Библиотека программиста публиковала конспекты и видеоподборки по линейной алгебре, математическому анализу и наглядному введению в нейросети. Ниже представлен незначительно сокращенный перевод публикации аналитика данных Хуен Тран о создании подобных анимаций с помощью Python.

Пример видео с канаал
***

Было бы здорово знать, как создавать подобные анимации, чтобы иметь возможность объяснить идеи из научного мира своим товарищам по команде, менеджерам или подписчикам. К счастью, автор канала собрал пакет manim (сокр. mathematical animation), который позволяет создавать математические анимации и изображения с помощью Python.

Пример построения с помощью manim
Пример построения с помощью manim

Библиотека стала довольно популярной: от оригинальной версии, созданной Грантом, сообществом Manim был сделан форк. Эта версия обновляется чаще и лучше тестируется, поэтому мы будем использовать её. Версия работает для Python 3.6+ и может требовать установки дополнительных зависимостей. В остальном всё как обычно:

        pip install manim
    

Приступим к созданию анимаций

Анимация растущего квадрата

Начнем с анимации квадрата, увеличивающегося в размерах из центра экрана. Код для создания анимации находится внутри метода construct в классе, наследованном от Scene.

start.py
        from manim import * 

class PointMovingOnShapes(Scene):
    def construct(self):
        square = Square(color=BLUE) # Cоздаем квадрат заданного цвета
        square.flip(RIGHT)
        square.rotate(-3 * TAU / 8) # Поворачиваем квадрат на -3/8 * 2*PI 

        # Проигрываем анимацию
        self.play(GrowFromCenter(square))
    

Сохраним скрипт под именем start.py и запустим в терминале команду, чтобы сгенерировать видео:

        $ manim -p -ql start.py PointMovingOnShapes
    

В результате скрипт создаст и сохранит видео под названием PointMovingOnShapes.mp4. Запустив его, мы увидим что-то вроде того, что показано ниже.

Результат анимации движения квадрата
Результат анимации движения квадрата

В команде для терминала использовались следующие опции:

  • -p: воспроизвести видео после того, как оно сгенерировано.
  • -ql: сохранить видео в низком качестве (меньший размер файла). Для генерации в высоком качестве используем -qh.

Чтобы вместо видео сгенерировать гифку, добавляем -i, например:

        $ manim -p -ql -i start.py PointMovingOnShapes
    

Превращаем квадрат в окружность

Создание квадрата не так уж интересно. Превратим его в окружность.

Трансформация фигуры
Трансформация фигуры
        from manim import * 

class PointMovingOnShapes(Scene):
    def construct(self):
        
        # Создаем квадрат
        square = Square(color=BLUE)
        square.flip(RIGHT)
        square.rotate(-3 * TAU / 8)
        
        # Создаем окружность
        circle = Circle()
        circle.set_fill(PINK, opacity=0.5) # устанавливаем цвет и прозрачность
      
        # Создаем анимации
        self.play(GrowFromCenter(square))
        self.play(Transform(square, circle))  # превращаем квадрат в круг
       
        self.wait() # ждем, чтобы картинка не сразу исчезала
    

Полный список фигур доступен в документации.

Настраиваем фон

Если нужно, чтобы фон имел другой цвет, задаем config.background_color.

Изменяем фон с черного на серый
Изменяем фон с черного на серый
        from manim import * 

config.background_color = DARK_GRAY
    

Другие возможности кастомизации описаны здесь.

Делаем динамическую рамку для частей уравнения

Подвижная рамка для выделения частей формулы
Подвижная рамка для выделения частей формулы
        class MovingFrame(Scene):
     def construct(self):
        # Пишем формулу
        equation = MathTex("2x^2-5x+2", "=", "(x-2)(2x-1)")

        # Создаем анимацию
        self.play(Write(equation))

        # Добавляем рамку
        framebox1 = SurroundingRectangle(equation[0], buff=.1)
        framebox2 = SurroundingRectangle(equation[2], buff=.1)

        # Создаем анимации
        self.play(Create(framebox1))  # создаем рамку

        self.wait()
        # replace frame 1 with frame 2
        self.play(ReplacementTransform(framebox1, framebox2))
    
        self.wait()
    

Можно показать, как пошагово решить уравнение:

Пошаговое решение
Пошаговое решение
        class MathematicalEquation(Scene):
    def construct(self):
    
        # Пишем уравнения
        equation1 = MathTex("2x^2-5x+2")
        eq_sign_1 = MathTex("=")
        equation2 = MathTex("2x^2-4x-x+2")
        eq_sign_2 = MathTex("=")
        equation3 = MathTex("(x-2)(2x-1)")

        # Размещаем каждое уравнение и знак по своим местам
        equation1.next_to(eq_sign_1, LEFT)
        equation2.next_to(eq_sign_1, RIGHT)
        
        eq_sign_2.shift(DOWN)
        equation3.shift(DOWN)
        
        # ДВыравнеиваем уравнения по знаку равенства
        eq_sign_2.align_to(eq_sign_1, LEFT)
        equation3.align_to(equation2, LEFT)

        # Группируем уравнения
        eq_group = VGroup(equation1, eq_sign_1, equation2, eq_sign_2, equation3)

        # Создаем анимацию
        self.play(Write(eq_group))
        
        self.wait()
    

Перемещаем и масштабируем формулы

Можно настроить положение «камеры» и область увеличения с помощью наследования класса от MovingCameraScene.

        class MovingAndZoomingCamera(MovingCameraScene):
    def construct(self):
        # Пишем уравнения
        equation = MathTex("2x^2-5x+2", "=", "(x-2)(2x-1)")

        self.add(equation)
        self.play(self.camera.frame.animate.move_to(equation[0]).set(width=equation[0].width*2))
        self.wait(0.3)
        self.play(self.camera.frame.animate.move_to(equation[2]).set(width=equation[2].width*2))

    

Рисуем графики

Библиотеку manim можно также использовать для создания аннотированных графиков.

        class Graph(GraphScene):
    def __init__(self, **kwargs):
        GraphScene.__init__(
            self,
            x_min=-3.5,
            x_max=3.5,
            y_min=-5,
            y_max=5,
            graph_origin=ORIGIN,
            axes_color=BLUE,
            x_labeled_nums=range(-4, 4, 2), # x tickers
            y_labeled_nums=range(-5, 5, 2), # y tickers
            **kwargs
        )
    
    def construct(self):
        self.setup_axes(animate=False)

        # Рисуем графики
        func_graph_cube = self.get_graph(lambda x: x**3, RED)
        func_graph_ncube = self.get_graph(lambda x: -x**3, GREEN)

        # Создаем метки
        graph_lab = self.get_graph_label(func_graph_cube, label="x^3")
        graph_lab2 = self.get_graph_label(func_graph_ncube, label="-x^3", x_val=-3)

        # Добавляем вертикальную линию
        vert_line = self.get_vertical_line_to_graph(1.5, func_graph_cube, color=YELLOW)
        label_coord = self.input_to_graph_point(1.5, func_graph_cube)
        text = MathTex(r"x=1.5")
        text.next_to(label_coord)
       
        self.add(func_graph_cube, func_graph_ncube, graph_lab, graph_lab2, vert_line, text)
        self.wait()
    

Если нужно получить изображение последнего кадра сцены, добавляем к команде -s:

        manim -p -qh -s more.py Graph
    

Можно сделать изображение более динамичным, если анимировать процесс настройки осей, установив animate = True.

        def construct(self):
        self.setup_axes(animate=True)
        ################### Остальной код такой же ################### 
    
        manim -p -qh more.py Graph
    
Анимация осей статичного графика
Анимация осей статичного графика

Совместное перемещение объектов

Чтобы группировать различные объекты и перемещать их вместе, используем VGroup.

Движение двух групп объектов
Движение двух групп объектов
        
class GroupCircles(Scene):
    def construct(self):

        # Создаем круги
        circle_green = Circle(color=GREEN)
        circle_blue = Circle(color=BLUE)
        circle_red = Circle(color=RED)
        
        # Устанавливаем начальные позиции
        circle_green.shift(LEFT)
        circle_blue.shift(RIGHT)
        
        # Добавляем две группы
        gr = VGroup(circle_green, circle_red)
        gr2 = VGroup(circle_blue)
        self.add(gr, gr2) # добавляем группы на сцену
        self.wait()

        self.play((gr + gr2).animate.shift(DOWN)) # сдвигаем обе группы вниз
        
        self.play(gr.animate.shift(RIGHT)) # смещаем одну группу отдельно
        self.play(gr.animate.shift(UP))

        self.play((gr + gr2).animate.shift(RIGHT)) # сдвигаем две группы вправо
        self.play(circle_red.animate.shift(RIGHT))
        self.wait()
    

Трассировка движения фигуры

Для создания следа движущегося объекта, применяем TracedPath:

Трассировка движения фигуры
Трассировка движения фигуры
        class TracedPathExample(Scene):
    def construct(self):
        # Создаем окружность и точку
        circ = Circle(color=BLUE).shift(4*LEFT)
        dot = Dot(color=BLUE).move_to(circ.get_start())

        # Группируем точку и окружность
        rolling_circle = VGroup(circ, dot)
        trace = TracedPath(circ.get_start)

        rolling_circle.add_updater(lambda m: m.rotate(-0.3))  # Вращаем окружность

        self.add(trace, rolling_circle) # add trace and rolling circle to the scene

        # Сдвигаем окружность на 8 смещений вправо
        self.play(rolling_circle.animate.shift(8*RIGHT), run_time=4, rate_func=linear)

    

Заключение

В этом небольшом туториале мы рассмотрели несколько способов, как можно анимировать математические абстракции с помощью библиотеки manim. Из приведенных примеров вы могли заметить, что manim предоставляет три вида объектов:

  • Mobjects: объекты, которые могут быть показаны на экране: Circle, Square, Matrix, Angle и т. д.
  • Scenes: сцены для построения анимации: Scene, MovingCameraScene и др.
  • Animations: анимационные эффекты, применяемые к Mobjects – такие, как Write, Create, GrowFromCenter, Transform.

Лучший способ научиться работать с такими объектами – делать собственные анимации. Другие примеры использования можно подсмотреть в руководстве по manim.

Скрипты из этой статьи также доступны в GitHub-репозитории.

***

На Python создают прикладные приложения, пишут тесты и бэкенд веб-приложений, автоматизируют задачи в системном администрировании, его используют в нейронных сетях и анализе больших данных. Язык можно изучить самостоятельно, но на это придется потратить немало времени. Если вы хотите быстро понять основы программирования на Python, обратите внимание на онлайн-курс «Библиотеки программиста». За 30 уроков (15 теоретических и 15 практических занятий) под руководством практикующих экспертов вы не только изучите основы синтаксиса, но и освоите две интегрированные среды разработки (PyCharm и Jupyter Notebook), работу со словарями, парсинг веб-страниц, создание ботов для Telegram и Instagram, тестирование кода и даже анализ данных. Чтобы процесс обучения стал более интересным и комфортным, студенты получат от нас обратную связь. Кураторы и преподаватели курса ответят на все вопросы по теме лекций и практических занятий.

Источники

МЕРОПРИЯТИЯ

Комментарии

ВАКАНСИИ

Добавить вакансию
Golang Developer
Москва, по итогам собеседования
Middle Product Manager
Москва, от 200000 RUB до 250000 RUB

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ