Показываем на примерах, как использовать навыки Python, чтобы сделать красивые математические анимации в духе YouTube-канала 3Blue1Brown. Обсудить 3Blue1Brown – популярный математический YouTube-канал Гранта Сандерсона. Мы и сами любим 3Blue1Brown за отличные объяснения и классные анимации. Библиотека программиста публиковала конспекты и видеоподборки по линейной алгебре, математическому анализу и наглядному введению в нейросети. Ниже представлен незначительно сокращенный перевод публикации аналитика данных Хуен Тран о создании подобных анимаций с помощью Python. Пример видео с канаал *** Было бы здорово знать, как создавать подобные анимации, чтобы иметь возможность объяснить идеи из научного мира своим товарищам по команде, менеджерам или подписчикам. К счастью, автор канала собрал пакет manim (сокр. mathematical animation), который позволяет создавать математические анимации и изображения с помощью Python. Пример построения с помощью 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-репозитории.
3Blue1Brown – популярный математический YouTube-канал Гранта Сандерсона. Мы и сами любим 3Blue1Brown за отличные объяснения и классные анимации. Библиотека программиста публиковала конспекты и видеоподборки по линейной алгебре, математическому анализу и наглядному введению в нейросети. Ниже представлен незначительно сокращенный перевод публикации аналитика данных Хуен Тран о создании подобных анимаций с помощью Python.
Пример видео с канаал ***
Было бы здорово знать, как создавать подобные анимации, чтобы иметь возможность объяснить идеи из научного мира своим товарищам по команде, менеджерам или подписчикам. К счастью, автор канала собрал пакет manim (сокр. mathematical animation), который позволяет создавать математические анимации и изображения с помощью Python.
Пример построения с помощью manim
Библиотека стала довольно популярной: от оригинальной версии, созданной Грантом, сообществом Manim был сделан форк. Эта версия обновляется чаще и лучше тестируется, поэтому мы будем использовать её. Версия работает для Python 3.6+ и может требовать установки дополнительных зависимостей. В остальном всё как обычно:
pip install manim
Приступим к созданию анимаций
Начнем с анимации квадрата, увеличивающегося в размерах из центра экрана. Код для создания анимации находится внутри метода construct в классе, наследованном от Scene.
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. Запустив его, мы увидим что-то вроде того, что показано ниже.
PointMovingOnShapes.mp4
Результат анимации движения квадрата
В команде для терминала использовались следующие опции:
-p
-ql
-qh
Чтобы вместо видео сгенерировать гифку, добавляем -i, например:
-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.
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.
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:
-s
manim -p -qh -s more.py Graph
Можно сделать изображение более динамичным, если анимировать процесс настройки осей, установив animate = True.
animate = True
def construct(self): self.setup_axes(animate=True) ################### Остальной код такой же ###################
manim -p -qh more.py Graph
Анимация осей статичного графика
Чтобы группировать различные объекты и перемещать их вместе, используем VGroup.
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:
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 предоставляет три вида объектов:
manim
Circle
Square
Matrix
Angle
Mobjects
Write
Create
GrowFromCenter
Transform
Лучший способ научиться работать с такими объектами – делать собственные анимации. Другие примеры использования можно подсмотреть в руководстве по manim.
Скрипты из этой статьи также доступны в GitHub-репозитории.
ΠΠ°Ρ Π°Π΄ΡΠ΅Ρ email Π½Π΅ Π±ΡΠ΄Π΅Ρ ΠΎΠΏΡΠ±Π»ΠΈΠΊΠΎΠ²Π°Π½. ΠΠ±ΡΠ·Π°ΡΠ΅Π»ΡΠ½ΡΠ΅ ΠΏΠΎΠ»Ρ ΠΏΠΎΠΌΠ΅ΡΠ΅Π½Ρ *
Π‘ΠΎΡ ΡΠ°Π½ΠΈΡΡ ΠΌΠΎΡ ΠΈΠΌΡ, email ΠΈ Π°Π΄ΡΠ΅Ρ ΡΠ°ΠΉΡΠ° Π² ΡΡΠΎΠΌ Π±ΡΠ°ΡΠ·Π΅ΡΠ΅ Π΄Π»Ρ ΠΏΠΎΡΠ»Π΅Π΄ΡΡΡΠΈΡ ΠΌΠΎΠΈΡ ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΠ΅Π².
Δ
ΠΡΠΎΡ ΡΠ°ΠΉΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ Akismet Π΄Π»Ρ Π±ΠΎΡΡΠ±Ρ ΡΠΎ ΡΠΏΠ°ΠΌΠΎΠΌ. Π£Π·Π½Π°ΠΉΡΠ΅, ΠΊΠ°ΠΊ ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°ΡΡΡΡ Π²Π°ΡΠΈ Π΄Π°Π½Π½ΡΠ΅ ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΠ΅Π².