Manual do Manim

Projeto PIBIC: Desenvolvimento de animações para o ensino de matemática usando o Manim

CNPq: 0220036212472856

Autor: Eric Satoshi Suzuki Kishimoto

Orientador: Prof. Vitor Rafael Coluci (Faculdade de Tecnologia/UNICAMP)

 

2.1.4 Operações com strings


Operações podem ser aplicadas não somente a números, mas também a textos. Em Python, temos as segiuntes operações:

Também temos a f-string que torna a concatenação de strings com variáveis mais simples. Colocamos um f antes da string.

 

2.1.5 Operaçõe booleanas


Temos também as operações booleanas. Basicamente, utilizamos operações booleanas para realizar comparações. Utilizamos comparações com os seguintes operadores.

Todas essas comparações podem resultar em apenas um dos resultados: True ou False.

 

Obs: Veremos na seção Estrutura de controle como usamos esssas expresões.

Além dessas comparações, podemos utilizar os operadores and, or e not para combinar as comparações ou tipos booleanos.

 

2.1.6 Operações de atribuição


Até agora, utilizamos um símbolo que não foi explicado, o =. Ele não funciona como o sinal de igual da matemática. Ele é um operador de atribuição, ou seja, usado para atribuir valor às variáveis. Por exemplo, no código a seguir:

var = 3

podemos interpretar que var recebe o valor 3. Há outras formas de realizar a atribuição de variáveis, por exemplo:

 

2.1.7 Comentários


Às vezes, o código pode ficar muito grande e complexo e seria interessante ter anotações sobre o que cada parte do código faz. Comentários não são executados, o que permite incluir anotações dentro do código. Para isso, usamos os comentários. Usamos o # Comantário para comentários de linha única e ''' comentário ''' ou """ comentário """ para comentário de múltiplas linhas.

 

2.1.8 Variáveis no Manim


Para começarmos a ver a conexão entre os conceitos vistos neste tópico e as animações produzidas pelo Manim, vamos criar uma pequena animação. Por exemplo, uma animação que armazena duas fórmulas f1 e f2 (armazenadas como variáveis), escreve f1 na tela e depois a transforma f1 em f2.

 

2.2 Mais tipos


Além dos tipos primitivos, existem outros tipos no Python que iremos abordá-los nesta seção.

 

2.2.1 Listas


Às vezes, queremos armazenar diversos valores em uma única variável. Para isso, usamos listas. Para criá-las, usamos colchetes ([]) e inserimos os valores separados por vírgulas ,. Chamamos cada valor da lista de elemento e podemos acessá-los através de índices (números inteiros começando do 0).

Podemos visulizar uma lista através da figura abaixo. Ela possui diversos elementos [3.14, 2.72, 0, 1, []] e podem ser acessados pelos índices 0 à 4 ou de trás para frente de -1 à -5.

Imagem de lista

 

Obs: Podemos ter listas dentro de listas como matrizes. Para acessar seus elementos, basta adicionar colchetes. Exemplo: matriz[0][0]

Além dessas operações, também podemos realizar algumas operações de conjuntos matemáticos:

 

2.2.2 Listas fatiadas


Vimos como acessar um elemento de uma lista, mas como acessar um intervalo? Para isso, usamos listas fatiadas. Inserimos um intervalo dentro do colchetes, separando o intervalo por dois pontos : como mostrado no exemplo a seguir:

 

Acima, o intervalo começa em 0 e termina em 3-1=2. Existem outras formas de fatiar uma lista.

 

2.2.3 Dicionários


Nas listas, acessávamos seus elementos através de números inteiros. Nos dicionários podemos usar além de inteiros, floats e strings. Para declarar um dicionário, basta usar chaves ({}).

 

Como podemos observar, dicionários são conjuntos de chave : valor. Podemos enxergar um dicionário com a figura a seguir. Nela, os valores à esquerda são as chaves e os valores à direita são os valores.

Imagem de dicionário

Podemos acessar essas chaves e valores com os métodos keys e values. Iremos explicar o que são métodos na seção de Programação Orientada a Objetos. Por agora, podemos considerar que são códigos que realizam tarefas sem sabermos sua implementação.

 

2.2.4 Tuplas


Também temos o tipo de dado tupla. Ela funciona da mesma forma que a lista. A única dferença é que não podemos mudar seus elementos. Declaramos uma tupla com parênteses (). Outra opção de declaração é a de separar os items entre vírgulas ,.

 

Como podemos perceber, todas operações que realizamos com listas podem ser realizadas com tuplas. Veremos mais a frente algumas coisas que são feitas apenas com tuplas.

 

2.2.5 Listas e Tuplas no Manim


Vamos ver um pouco como utilizar listas e tuplas no Manim. É muito comum mover objetos gráficos no Manim. Para isso, utilizamos listas para representar as coordenadas desses objetos.

 

2.3 Estruturas de controle


Agora que vimos os tipos de dados, precisamos entender como manipulá-los. Para isso, usaremos as estruturas de controle. Abordaremos aqui as estruturas if, else, elif, while e for.

 

2.3.1 if else


A primeira estrutura de controle que abordaremos será o if. Basicamente o que ele faz é verificar se uma condição é verdadeira. Se for, o código dentro é executado. Se não for, o código não é executado.

 

Acima, verificamos se a variável num é par, ou seja, se o resto da divisão por 2 é 0. Se for, o código imprime “2 é par”.

Obs: tabulações são usadas para colocar um código dentro de uma estrutura

Agora que aprendemos o se (if), precisamos do se não (else). Basicamente, se a condição no if for falsa, ela é redirecionada para o else.

 

De forma gráfica, podemos ver essa estrutura da seguinte forma.

Estrutura de controle if else

 

2.3.2 elif


Uma estrutura complementar ao if else é o elif. Basicamente, em vez de apenas 2 condições, podemos encadiar várias delas.

Estrutura de controle elif

 

2.3.3 while


Existem situações onde queremos que o mesmo trecho de código se repita diversas vezes. Para isso, usamos o laço (loop) while. Por ele, passamos uma comparação que executa um código até que a condição seja falsa.

Estrutura de controle while

 

Obs: Tome cuidado apenas quando a expressão for sempre verdadeira. Isso é o que chamamos de loop infinito. Nestes casos, ela será executada até que aconteça um erro. Esse erro acontece pois a memória – que é onde os dados do computador são armazenados – fica toda ocupada. Não abordaremos com profundidade como a memória do computador funciona, mas ela não é ilimitada. Entretanto, não precisamos nos preocupar tanto com isso. Basta evitarmos loop infinitos.

Obs: O loop while não é tão utilizado pois a estrutura a seguir que veremos (for) nos permite fazer a mesma coisa de forma mais fácil. Porém, é bom saber como o while funciona.

 

3.3.4 break


Se quisermos sair da estrutura while mesmo que a condição ainda seja verdadeira, usamos a expressão break.

 

2.3.5 for


Outra estrutura que abordaremos é o loop for. Nele, iteramos os elementos de uma lista. Não está escrito errado, iterar significa passar por cada um dos elementos de uma lista ou estruturas com diversos elementos. Usamos o for junto com o in.

 

Obs: Ambos while e for são estruturas de repetição, ou seja, eles repetem a parte do código diversas vezes. Normalmente o for é mais utilizado por por ser mais simples de escrever. No while, precisamos criar uma variável para contar quantas vezes e ainda precisamos incrementá-la. No for, precisamos apenas de uma lista ou do range que veremos no próximo tópico.

 

2.3.6 range


Além de usar listas, podemos usar o range para iterar elementos. Ele basicamente cria uma sequencia de números.

 

Também podemos adicionar outros números para indicar o começo, o fim e o passo, como fazemos nas listas fatiadas.

 

2.3.7 Compreensões de listas


Agora que sabemos como usar o loop for, vamos ver uma forma mais simples de criar uma lista. Podemos criá-la com um loop for de forma simples, como mostrado no exemplo abaixo.

 

2.3.8 Operador ternário


Podemos usar a estrutura do if else em uma só linha. Usamos o operador ternário para isso.

 

2.4 Funções


Até agora, vimos tudo que precisamos para criar nossos próprios programas. Entretanto, ter diversas linhas de código iguais é ineficiente. Para isso, utilizaremos funções.

 

2.4.1 Reuso de código


Muitas vezes, repetimos o mesmo código diversas vezes. Por exemplo, quando queremos fazer a média de 3 listas.

 

Estamos repetindo o mesmo código diversas vezes. Para evitar isso, usamos funções.

 

Como podemos observar, a quantidade de linhas de código foi bastante reduzida. Uma função é declarada apenas uma vez e pode ser chamada quantas vezes forem necessárias, diminuindo consideravelmente a quantidade de linhas de código.

Obs: a função acima possui grande parte dos elementos de uma função que veremos a seguir.

Em programação, o uso de funções é muito importante. Nos exemplos desse manual, são abordados exemplos simples com poucas linhas de código. Mas, em códigos como as das animações do canal 3b1b, podem haver centenas ou milhares de linhas de código. Se não fossem usadas funções, haveria muito mais linhas de código e possivelmente estaria muito mais desorganizado.

 

2.4.2 O que são funções?


Até agora discutimos qual a importância das funções e do reuso do código, mas o que exatamente são funções? São pedaços de código que podem ser reutilizados pelo programa. Também podemos utilizar a definição matemática que é algo que recebe entradas e que fornece saídas. Já utilizamos algumas funções neste manual como o print e o range.

 

Essas funções são pré-definidas pelo Python, mas podemos criar nossas próprias funções usando a palavra-chave def.

 

Podemos fazer 2 coisas com uma função: definí-la e chamá-la. Acima, usamos o def para definí-la e a chamamos digitando seu nome seguido de parenteses ().

 

2.4.3 Escopo


Escopo é um conceito que deixa muitas pessoas confusas quando estão vendo programação pela primeira vez. Mas podemos definí-lo como o lugar onde variáveis ou funções estão no código. Podemos ter um escopo mais “aberto” ou um mais “fechado”.

 

Ao executarmos o código acima, teremos um erro dizendo que a variável var_interna não está definida. Mas definimos ela dentro da função. Isso acontece porque definimos ela dentro de um escopo mais fechado e o escopo mais aberto não consegue enxergar variáveis.

Obs: não se assuste com erros, eles aparecerão com muita frequência enquanto você criar os códigos das animações.

Para simplificar as coisas podemos definir que:

Escopo mais aberto não consegue enxergar variáveis ou funções em escopos mais fechados.

Obs: quando uma função termina, todas suas variáveis internas são destruídas.

 

2.4.4 Argumentos


Anteriormente indicamos que funções podem receber entradas. Para isso, informamos essas entradas dentro de parenteses.

 

Obs: Argumentos são definidos como as entradas quando declaramos a função. Já os parâmetros são os valores que passamos para função. Essa definição não é obrigatória e podemos usar apenas argumentos ou parâmetros para essas duas definições.

 

2.4.5 Parâmetros Padrão


Se quisermos usar uma função sem precisar ficar toda hora passando parâmetros, podemos criar parâmetros padrão que substituirão os valores dos argumentos se os parâmetros não forem passados.

 

Obs: os parâmetros padrão devem ser os últimos da definição da função. Caso contrário, ocorrerá um erro.

 

2.4.6 Parâmetros nomeados


Em funções com muitos parâmetros, podemos ficar confusos quanto à ordem dos argumentos. Para simplificar o processo, usamos parâmetros nomeados na chamada da função e, com isso, podemos passar o nome do parâmetro que queremos.

 

Obs: Parâmetros não nomeados são chamados de parâmetros posicionais, uma vez que dependem da posição onde são passados.

Obs: Parâmetros posicionais devem ser passados antes dos nomeados.

 

2.4.7 Retorno


Até agora, apenas passamos as entradas para a função e as usamos dentro da função. Isso não é recomendado pois precisamos usar os resultados calculados dentro da função fora dela. Para isso, usamos a palavra-chave return, ou seja, a saída da função.

 

Obs: Temos que tomar cuidado com o tipo de retorno da função. Se passarmos esse retorno como parâmetro de outra função, temos que verificar qual o tipo que estamos passando.

Obs: Precisamos colocar o parênteses quando chamamos a função. Se não o fizermos, ela retornará um objeto function. Veremos mais sobre objetos na seção Programação orientada a objetos.

 

2.4.8 Desempacotamento de listas e tuplas


Esse tópico é tratado como avançado para quem aprende a programar por ser uma ferramenta única do Python. Entretanto, é um conceito muito importante para a parte de funções do Python. Esse conceito permite diversas coisas, entre elas:

 
 
 

2.4.9 `lambda`


Uma forma de declarar uma função sem ter que utilizar diversas linhas de código é usar a expressão lambda.

 

Obs: Em tópicos anteriores, criamos a função somatorio manualmente, mas ela já existe como uma das funções padrão do Python.

 

2.5 Trabalhando com arquivos


Nesta seção, veremos como trabalhar com arquivos. Veremos como abrir, escrever e ler arquivos com o Pytohn.

 

2.5.1 open


Para criar e ler arquivos, usamos a função open. Ela possui 2 argumentos:

Depois que abrimos um arquivo e executamos as operações desejadas com ele, devemos fechá-lo para que não haja problemas posteriores. Para isso, usamos o close.

 

2.5.2 Escrevendo arquivos


Vimos anteriormente que podemos abrir o arquivo em modo w (write) ou a (append). Agora, veremos o que podemos fazer com isso. Usamos a função write para sobrescrever o arquivo (w) ou para adicionar conteúdo ao final do arquivo(a). Se o arquivo não existir, ele é criado.

 

2.5.3 Lendo arquivos


Existem duas funções principais para ler arquivo.

 

Obs: \n é um caracter de escape. Ele significa uma quebra de linha.

 

2.5.4 Trabalhando com arquivos


Nos últimos tópicos, tivemos que chamar a função open e close diversas vezes. Para que não dependamos disso, usaremos a estrutura with .... as. Nela, uma variável temporária será criada para manipularmos o arquivo. Com isso, não precisamos usar a função close.

 

2.6 Programação Orientada a Objetos


Na história da programação, existiram diversos paradigmas. Por exemplo, os que vimos até agora foram:

Nesta seção, abordaremos um novo e muito famoso paradigma, o paradigma da orientação a objetos. Ele é muito importante para o Python, onde tudo que criamos é um objeto, e para o Manim, que trabalha com esse paradigma.

 

2.6.1 Conceitos iniciais


O paradigma da Programação Orientada a Objetos (POO) foi criado por Alan Kay e tinha como objetivo aproximar o mundo real do mundo da programação.

Mas o que são objetos?

Em POO, podemos definir objetos como qualquer coisa concreta ou abstrata que possui:

  • propriedades/atributos: coisas que o objeto tem
  • comportamento/métodos: coisas que o objeto faz
  • estado atual: como o objeto está

Por exemplo, imaginemos um carro. Ele possui as seguintes características.

A Orientação a Objetos possui 4 pilares:

Veremos o que essas definições significam.

 

2.6.2 Objeto e classe


Podemos pensar num objetos como uma variável cujo tipo é a classe. Nela, podemos definir seus atributos, que são variáveis que ela possui, e seus métodos, que são funções que ela pode chamar.

 

Acima, criamos uma classe chamada Carro com

Obs: todos os métodos da classe devem ter o parâmetro self, que é um parâmetro que faz referência ao próprio objeto. No exemplo acima, só conseguimos mudar a velocidade do carro por causo do parâmetro self

Obs: Podemos definir atributos como variáveis de um objeto e método como funções de um objeto.

Obs: as classes devem ser nomeadas com nomes começando com letra maiúscula. Ser for usada mais do que uma palavra no nome, a separação das palavras é feita iniciando a primeira letra da próxima palavra em maiúsculo. Ex: MeuCarro

 

2.6.3 Construtor


Até agora, criamos uma classe, mas não um objeto. Antes de “instanciar” um objeto, precisamos definir seu construtor. O construtor de uma classe define o que ela fará quando for instanciada. Normalmente usamos o construtor para inicializar os seus atributos. Em Python, usamos o método __init__ para definir o construtor. Com o construtor definido, podemos instanciar um objeto a partir de sua classe.

 

Acima, criamos uma classe que inicializa sua cor e modelo com parâmetros passados pelo construtor e inicializamos a sua velocidade atual em 0. Depois, imprimimos sua cor, modelo e velocidade.

 

Vamos demonstrar como o Manim usa classes e objetos. Criaremos uma cena animando um quadrado e transformando-o em um círculo.

 

2.6.4 Encapsulamento


Quando criamos um objeto, podemos acessar seus atributos diretamente. Porém, esse acesso não é uma boa ideia e pode até ser perigoso. É necessário esconder esses dados e criar métodos para acessar esses atributos.

Para esconder os dados colocamos um _ ou __ antes das variáveis.

 

Como podemos observar, ao colocarmos __, não é mais possível acessar o atributo de fora da classe. Para acessá-la, usaremos o property. Chamamos esse método de método acessor ou mais conhecido como getter.

 

Agora, podemos acessar o atributo nome de pessoa, mas não podemos modificá-lo. Para isso, usaremos o .setter. Esse método se chama método modificador ou mais conhecido como setter.

 

Com isso, podemos modificar o atributo __nome. Encapsulamos atributos para manter a segurança. O método getter retorna apenas uma cópia do valor do atributo e não o atributo em si.

 

2.6.5 Herança


Com a herança, podemos compartilhar funcionalidades entre classes. Quando criamos uma classe, podemos herdar seus atributos e métodos de outra classe. Chamamos a classe que compartilha os atributos e métodos de classe pai. Chamamos a classe que herda os atributos e métodos de classe filha. Usamos esse conceito quando diversas classes possuem algo em comum.

Por exemplo, temos classes cachorro e gato. Todas elas possuem o atributo nome. Em vez de criar um atributo nome em todas essas classes, podemos criar uma classe animal com atributo nome e todas as outras classes irão herdar dessa classe. Neste caso, animal é a classe pai e as demais são as classe filhas.

Para que as classes filhas herdem da classe pai, deve-se colocar a classe pai entre parenteses na frente da classe filha.

 

Como podemos observar, ao criar um cachorro e um gato, podemos acessar o atributo nome de cada um deles pois ambos herdam de Animal.

 

Também podemos acessar a classe pai dentro da classe filha usando o método super. Usamos isso para chamar métodos da classe pai.

 

2.6.6 Polimorfismo


Outro conceito importante na POO é o polimorfismo. Basicamente, podemos modificar o comportamento de uma função da classe pai. Assim, diversas classe filhas possuem a mesma assinatura (nome), mas fazem coisas diferentes, embora semelhantes. Ou seja, é possível sobrescrever métodos das classes pai.

Do exemplo anterior, podemos criar um método acelerar na classe pai e mudá-lo na classe filha.

 

Como podemos observar, a classe Veiculo possui o método acelerar, mas as classes filhas modificam o comportamento desse método.

 

2.6.7 Operadores mágicos


Em vez de chamarmos métodos, podemos usar operadores como +, -, *, /, entre outros. Para isso, usamos operadores mágicos. Por exemplo, se tivermos uma classe Vetor, não podemos apenas usar o sinal de + para somar 2 vetores. Podemos usar o método __add__ para isso.

 

Podemos utilizar os seguintes operadores mágicos:

 

Obs: a diferença entre o método de classe e o método estático é que o de classe tem como primeiro parâmetro cls que é o construtor da classe enquanto o método estático não tem.

 

Funções como objetos


Funções também são objetos. Elas podem ser atribuídas a variáveis passando apenas seu nome sem parenteses. Isso pode ser útil para passarmos funções como argumentos de outras funções.

 

2.6.10 POO no Manim


Agora, vamos demonstrar como a POO é aplicada no Manim. Vamos criar uma animação que ilustra a manipulação de uma equação de 1$^o$ grau para determinar o valor da incógnita.

 

Nesse exemplo, vimos um exemplo dos tópicos:

Não usamos os outros, mas eles podem aparecer em outros códigos.

 

2.7 Bibliotecas


Utilizamos bibliotecas para modularizar o código e usar funcionalidades de terceiros. No nosso caso, usaremos a biblioteca Manim para as animações mas, antes, entenderemos como elas são criadas e como instalá-las.

 

2.7.1 O que são bibliotecas?


Bibliotecas são arquivos com códigos que serão utilizados por outros códigos. É um conjunto de classes e funções para ajudar a desenvolver programas de forma mais fácil, simples e rápida.

 

2.7.2 Como importar bibliotecas


Para importar uma biblioteca, utilizamos a palavra-chave import. Ao importarmos, a biblioteca é tratada como um objeto e podemos chamar suas funções ou objetos. Vamos importar a biblioteca numpy.

 

Podemos renomear a biblioteca utilizada no nosso código. Vamos renomear numpy para np.

 

Se quisermos algo que está dentro da biblioteca, usamos a palavra-chave from. Importaremos pyplot de matplotlib e o renomearemos para plt.

 

2.7.3 Como criar sua própria biblioteca


Para criar uma biblioteca, basta criar um arquivo Python com as funcionalidades desejadas. Para importá-lo, basta usar o import com o nome do arquivo.

Por exemplo, se criarmos o arquivo bib.py, ele será importado com:

import bib

Para criar bibliotecas em uma pasta, é necessário criar um arquivo chamado __init__.py e para importá-lo, basta usar o import no nome da pasta.

Por exemplo, se criarmos uma pasta bib, precisamos criar um arquivo __init__.py e importá-lo com:

import bib
 

2.7.4 args e kwargs


Nas bibliotecas, exitem muitas classes e hierarquias de classe, várias classes pais e suas filhas. Com isso, para criar os construtores, precisa-se de muitos argumentos. Para que não haja argumentos em excesso, usamos o args e kwargs. Já utilizamos o args em tópicos passados. O kwargs permite que utilizemos parâmetros nomeados passando dicionários. Esses dois parâmetros são muito usados no Manim.

 

Com isso, não precisamos digitar o nome de todos os argumentos no construtor. Muitas bibliotecas externas também fazem como o Manim.

 

2.7.5 pip


Para instalar bibliotecas de terceiros, utilizamos o gerenciador de pacotess pip. Com ele, podemos instalar, desinstalar e gerenciar bibliotecas listadas no PyPI (Python Package Index). As bibliotecas listadas no PyPI são chamados pacotes e são todos open source, ou seja, livre para uso não comercial dependendo de sua licença.

Para instalar pacotes, utilizamos o comando:

pip install 

Para desinstalar pacotes, utilizamos o comando:

pip uninstall 

Para listar pacotes instalados, utilizamos o comando:

pip list

Para criar um arquivo para exportação com todos os pacotes usados, utilizamos o comando:

pip freeze > requirements.txt

Para importar pacotes do requirements.txt, utilizar o comando:

pip install -U -r requirements.txt
 

2.7.6 venv


Conforme vamos instalando bibliotecas, todos os scripts usados para fazer as animações podem ver todas as bibliotecas. Isso pode deixar o projeto da animação desorganizado. Para isso, utilizamos ambientes virtuais onde instalamos as bibliotecas apenas nesse ambiente. Para criarmos esses ambientes, usaremos a virtualenv ou venv.

A venv já vem por padrão desde o Python 3.3. Para criar um ambiente virtual, usar o comando no terminal

python -m venv 

Para ativar o ambiente depois de criado, usar o comando

/Scripts/activate

Com isso, sempre que instalarmos novos pacotes, eles serão instalados dentro desse ambiente virtual. Note que ele irá criar uma estrutura de pastas com:

Para mais informações, consultar a documentação da venv.

 

2.7.7 _requirements_


No tópico pip, vimos como exportar pacotes usados, mas não explicamos o que isso significa. No Python, podemos salvar todas as bibliotecas usadas em um arquivo requirements.txt e se alguém precisar instalar todos esses pacotes, basta usar o comando.

pip install -U -r requirements.txt

Obs: requirements.txt é o nome padrão desse arquivo

 

2.7.8 numpy


Uma biblioteca muito utilizada no Manim é o numpy. Ela permite a criação de listas mais rápidas, vetores que são utilizados na biblioteca. Para criar um vetor, basta usar o comando array.

 

Também existem outras funções e objetos nessa biblioteca. Alguns exemplos são dados a seguir:

 

Para instalar o numpy localmente, utilizamos o comando:

pip install numpy

Para mais informações, consulte a documentação oficial do numpy

 

3. Introdução ao Manim


Agora que vimos como trabalhar com o Python, desde coisas mais básicas(if, else, for) até mais avaançadas (desempacotamento de tuplas, decoradores), vamos adiante com a biblioteca Manim.

 

3.1 Instalação Local


No momento, estamos usando o Manim no ambiente em nuvem do Google Colab, mas a maior parte dos projetos são criados localmente com arquivos Python. Para usar o Manim localmente, precisamos instalá-lo localmente. Vamos utilizar o ManimCE (Manim Community Edition).

Obs: não é necessário instalar o Manim para seguir com o Manual.

O Manim utiliza 2 softwares de linha de comando externos:

 

Para instalar no Windows, usaremos o gerencador de pacotes chocolatey. Para instalá-lo, abrir o powershell como administrador e usar o comando:

Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))

Com o chocolatey instalado, podemos instalar o Manim simplesmente com o comando:

choco install manimce

Se isso não funcionar, instalar o ffmpeg, Latex e Manim manualmente.

Com eles instalados, criar ambiente virtual (venv) e instalar o Manim com o comando:

pip install manim

Caso ocorrer erro, recomeçar os passos.

Para mais informações consultar documentação.

 

Para instalar no Linux, em Ubuntu, Mint ou Debian, usar os comandos:

Se isso não funcionar, repetir os passos.

Para mais informações consultar documentação.

 

Para instalar no MacOS, usar os comandos:

Para mais informações, consultar documentação.

 

3.2 Estrutura da cena


Agora, começaremos a entender como o Manim funciona. Usamos código que o Manim interpreta e transforma em um vídeo. Para que ele identifique o que interpretar, usamos a classe Scene. Criamos uma classe que herda de Scene e o Manim o identifica. Para construir as animações, usamos o método construct herdado de Scene. Para o Manim identificar as animações a serem renderzadas, usamos o método play da superclasse Scene. Assim, o Manim encontra todas as classes que herdam de Scene, econtra o método construct e renderiza todos os métodos play. Já vimos alguns exemplos e vamos à mais um.

 

3.3 Parâmetros de linha de comando


Aqui no Google Colab, utilizamos células mágicas, que são aquelas que começam com class Texto(Scene): def construct(self): texto = Text('Isso é um texto') self.add(texto)

 

Obs: usamos o método add para adicionar um objeto gráfico à cena resultando em apenas uma imagem.

Podemos mudar a fonte do texto usando o argumento font.

 

Para mudar a formatação do texto em normal e itálico, usar o argumento slant com os parâmetros NORMAL e ITALIC.

 

Também é possível mudar a cor com o argumento color.

 

Também podemos mudar a cor do texto com o MarkupText. Ela renderiza um texto em formato Markup que é uma linguagem que permite formatar o texto. Para definir um bloco do texto que queremos editar, usamos as tags com a propriedade fgcolor como no exemplo a seguir.

 

Tamém podemos criar gradientes de cores com o argumento gradient.

 

4.2 Latex


Agora que vimos como usar textos no Manim, vamos ver como utilizar as fórmulas e expressõers matemáticas. Para isso, usaremos o Latex, uma linguagem de marcação muito usada para escrever artigos científicos e que permite escrever documentos e renderizar expressões matemáticas. Para aprender mais sobre o Latex, consulte o seguinte material.

Se você não tem muita prática com o Latex, você pode usar o seguinte site para criar as fórmulas matemáticas.

 

Para renderizar as equações aqui no Google Colab, colocamos a expressão entre $. Vamos ver algumas expressões que podem ser criadas no Latex.

x &= 3 + 2 + 1

x &= 5 + 1

x &= 6

$f(x) = 3 + 2 + 1\\ = 5 + 1 \\ = 6$

\begin{bmatrix}

1 & 2 & 3\\\\

4 & 5 & 6\\\\

7 & 8 & 9

\end{bmatrix}\begin{bmatrix}
1 & 2 & 3\\
4 & 5 & 6\\
7 & 8 & 9
\end{bmatrix}\begin{pmatrix}

1 & 2 & 3\\\\

4 & 5 & 6\\\\

7 & 8 & 9

\end{pmatrix}\begin{pmatrix}
1 & 2 & 3\\
4 & 5 & 6\\
7 & 8 & 9
\end{pmatrix}

 

4.3 MathTex

 


Agora que sabemos usar o Latex para expressões matemáticas, vamos entender como usá-las no Manim. Para criar um objeto gráfico, usamos o objeto MathTex que será renderizado na tela, passando o códiog Latex.

 

Também podemos separar um MathTex como uma lista separando os textos por vírgulas.

 

4.4 `Tex`


O Latex também possui o modo texto. Usamos o comando \text{} para isso. No Manim, podemos usar o Tex para isso. Podemos utilizá-lo com listas igual ao MathTex.

 

4.5 `geometry`


Agora, veremos alguns objetos geométricos como retângulos e retas.

Obs: não abordaremos todos os objetos geométricos. Para mais informações, consulte documentação.

 

4.5.1 `Square`


Para começar vamos ver o objeto Square, ou seja, quadrado. Passamos o tamanho do lado para ele.

 

4.5.2 `Rectangle`


O Rectangle é usado para criar um retângulo. Passamos os tamanhos de seus lados.

 

4.5.3 `Circle`


Usamos o Circle para criar um círculo. Passamos o seu raio.

 

4.5.4 `Ellipse`


Usamos o Ellipse para criar uma elipse. Passamos os valores do semi-eixo maior e menor.

 

4.5.6 Coordenadas no Manim


Antes de apresentarmos mais objetos, vamos entender como o sistema de coordenadas no Manim funciona.

No Manim, as coordenadas são dadas através de um numpy.array de 3 elementos que representam as coordenadas $x$, $y$ e $z$. Mesmo em planos bidimensionais, são passadas as 3 coordenadas. Nesses casos, o terceiro elemento é passado como 0. Por exemplo: np.array([1, 2, 0])

Mesmo que o sistema de coordenadas esteja em numpy.array, ainda é possível utilizar listas ou tuplas de 3 elementos quando especificarmos a posição dos objetos gráficos na tela. As coordenadas funcionam no sistema $(x, y, z)$, ou seja, $(0, 0, 0)$ é o centro da tela. Se aumentarmos $x$, movemos as coordenadas para direita e, se aumentarmos $y$, movemos as coordenadas para cima.

Para facilitar e não ter que escrever as coordenadas, é possível usar direções (constantes) que especificam essas coordenadas de maneira mais “humana”. Existem as constantes:

Podemos pensá-las como os vetores na matemática, ou seja, podemos somá-las e multiplicá-las por um número real.

Podemos observar as extremidades da tela no Manim com o seguinte código. Se não entender algo, não se preocupe pois vamos abordar o que não for abordado.

 

4.5.7 `Line`


Usamos o Line para criar uma seta, informando as coordenadas de início e de fim.

 

4.5.8 `Arrow`


Usamos o Arrow para criar uma seta, informando as coordenadas de início e de fim.

 

4.5.9 `Dot`


Usamos o Dot para criar um ponto, especificando suas coordenadas.

 

4.6 Manipular Mobjects


Agora que vimos como criar Mobjects, veremos como manipulá-los, ou seja, mudar cor, posição, tamanho, girar, entre outros.

 

4.6.1 `move_to`


Uma das formas de mover um objeto é com o método move_to. Especificamos suas coordenadas e ele move o Mobject.

 

4.6.2 `next_to`


Outra forma de mover objetos é com o next_to. Como o próprio nome já diz, “próximo à”. Ou seja, especificamos um objeto, em que sentido queremos deixar o objeto e a distância entre os objetos.

 

4.6.3 `shift`


Outra forma de mover objetos é usando o shift. Ele move o objeto em relação à sua posição atual. É o método mais simples.

 

4.6.4 `to_corner`


A última forma de mover objetos que veremos é o to_corner. Ele move o objeto para um dos cantos da tela. Basta passar a posição e ele acha o canto.

 

4.6.5 `scale`


Para mudar o tamanho dos objetos, usamos o método scale. Passamos um número que representa o quanto queremos aumentar o objeto. Se passarmos 2, o objeto aumentará 2 vezes.

 

4.6.6 `rotate`


Também podemos girar um objeto com o rotate. Passamos o ângulo em radianos para o método e ele gira o objeto. Podemos usar o DEGREES para converter para graus.

 

4.6.7 `set_color`


Podemos alterar a cor dos objetos com o set_color. O Manim possui constantes com algumas cores pré-definidas. Também podemos especificar cores em hexadecimal.

Obs: Para mais informações, consultar documentação.

Obs: Para passar a cor em hexadecimal, usar uma class Cor(Scene): def construct(self): quadrado = Square().set_color(RED) self.add(quadrado)

 

4.6.8 `become`


No Manim, quando alteramos os atributos de um objeto, nem sempre isso se reflete na tela. Para isso, usamos o método become que transforma um objeto em outro.

 

4.6.9 `copy`


Uma forma de criarmos cópias de objetos é com o método copy. Isso não é um método do Manim, mas do próprio Python. Todos os objetos possuem esse método.

 

4.7 `VGroup`


É possível agrupar Mobjects usando listas. Entretanto, há um jeito melhor para fazer isso. Para criar um grupo de Mobjects, usamos o VGroup. Nele, colocamos todos os objetos que queremos agrupar. Ele é muito útil quando queremos manipular vários objetos que compõem um todo.

 

5. Animações


Agora que vimos vários elementos gráficos, vamos começar com as animações. Como foi visto anteriormente, animamos uma cena utilizando o método construct. Dentro dele, chamamos o método play e a animação que queremos renderizar. Veremos essas animações nesta seção. Em todas elas, passamos o objeto que queremos animar e a animação faz o resto do trabalho. No play, também podemos passar um parâmetro run_time que é o tempo que a animação vai durar.

 

5.1 `Write`


A primeira animação que veremos é o Write. Ela anima um objeto com se estivesse escrevendo-o como o próprio nome diz.

 

Obs: É normal esquecer de passar a animação e passar apenas um Mobject pelo play. Nesses casos, aparecerá um erro que alertará o usuário que não há animação sendo passada.

 

5.2 `FadeIn`


O FadeIn anima o objeto como se estivesse aparcendo na tela.

 

5.3 `FadeOut`


Ao contrário das animações que vimos até agora, o FadeOut faz o objeto gráfico desaparecer.

 

5.3 `Transform` e `ReplacementTransform`


Agora, vamos ver animações que pegam objetos e os transformam em outros. O Transform e ReplacementTransform cumprem esse papel. Passamos o objeto inicial e o final.

 

A diferença entre o Tranform e o ReplacementTransform é que, para manipular os objetos depois da transformação com o:

 

Se não fizermos isso, coisas estranhas acontecerão!

 

5.4 Animações de manipulação


A manipulação de objetos, movendo-os ou alterando seus tamanhos, não acontece em forma de animação. Para animar esse tipo de manipulação, é necessário usar o atributo animate.

 

Em outras versões do Manim como o Manimcairo e o ManimGL, passamos o método e seus parâmetros. Não conseguimos fazer isso aqui pois esse método foi depreciado. Mas podemos demonstrar como fazer isso com o objeto ApplyMethod.

 

5.5 Outras animações


Vimos diversas animações até agora, mas ainda existem diversas outras, basta explorá-las. Para mais informações consulte a documentação.

 

6. Gráficos 2D


Alguns Mobjects que não vimos são os gráficos. Existem alguns objetos que os repressentam que veremos nesta parte.

 

6.1 `NumberLine`


O primeiro objeto que veremos é o NumberLine. Como o próprio nome já diz, ele representa uma linha (reta) de números (reais). Ela possui um construtor com diversos parâmetros, mas os principais são:

 

Temos alguns métodos úteis:

 

6.2 `Axes`


O NumberLine representa um eixo, já a classe Axes representa 2 eixos (perpendiculares), ou seja, o plano cartesiano. Possui argumentos parecidos com o do NuumberLine, porém, existem os eixos $x$ e $y$.

Não precisamos passar nenhum parâmetro pelo construtor pois todos os seus parâmetros são opcionais.

 

No código fonte, ele é criado utilizando 2 objetos NumberLine, ou seja, podemos pegar cada uma das retas que representam os eixos e usar seus métodos. Para isso, usamos o método get_axis. Ele retorna uma lista com os 2 NumberLine.

 

Ainda temos alguns métodos interessantes para utilizarmos.

 

7. Update functions


O Manim possui diversas animações pré-definidas, porém, é possível criar nossas próprias animações. Para isso, usamos as update functions.

 

7.1 Animações _frame_ a _frame_


As primeiras animações (como as de desenhos e filmes) eram feitas desenhando e gravando diversas imagens (ou mais popularmente conhecidas como frames), dando a sensação de movimento. Esse processo é feito até hoje! Entretanto, hoje em dia, existem ferramentas que automatizam uma parte deste processo. Esse tipo de processo também pode ser feito no Manim, utilizando o wait.

 

7.2 Updaters


Em vez de criarmos animações no Manim desse jeito, uma opção melhor é usarmos updaters. São funções chamadas uma vez por frame renderizado. Criamos uma função chamada updater que tem como argumentos um Mobject mob e um dt que explicaremos depois. Dentro desta função, manipulamos o Mobject da forma que quisermos. Depois de criada, usamos o método add_updater e passamos a função updater que faz o resto do trabalho. Depois disso, usamos a função wait para especificar por quanto tempo queremos que dure a animação. Para parar a animação, usamos o remove_updaters ou clear_updaters.

 

Há algumas situações que não podemos atualizar alguma propriedade do objeto. Por exemplo, não podemos mudar os pontos de início e fim do objeto Line. Para isso, transformamos o objeto em outro Line utilizando o become dentro da função updater.

 

Obs: point_from_proportion é um método de Mobjects que aceita um número de 0 a 1 e retorna a posição proporcional ao número passado. Se o Mobject for um círculo e passamos 0.5 para o método, ele retornará o ponto do círculo à 180º.

 

7.3 `mob` e `dt`


Agora que vimos como utilizar os updaters, vamos entender o que os argumentos mob e dt são. O argumento mob é aquele que recebe o objeto que o updater está chamando. Por exemplo, se tivermos o seguinte código:

ponto = Dot()

def updater(mob, dt):
  # código do updater

ponto.add_updater(updater)

O objeto ponto será passado como mob para a função updater.

Mas e o dt? Ao chamar o método add_updater, a função passada como parâmetro será chamada a cada frame, ou seja, se o vídeo tiver mais quadros/frames por segundo ($N_{\text{fps}}$), a animação será mais rápida. Para que isso não aconteça, usamos o dt que possui valor $\displaystyle \frac{1}{N_{\text{fps}}}$. Com isso, se renderizarmos em diferentes $N_{\text{fps}}$, vamos obter o mesmo resultado quanto à velocidade da animação.

 

Por exemplo, uma animação com $N_{\text{fps}}=15$ fps (frames por segundo) (opção -ql):

 

Agora uma animação com $N_{\text{fps}}=60$ fps (opção -qh).

 

8. Produção de animações no Manim


Agora que vimos como utilizar o Manim com exemplos básicos, vamos ver um pouco como criar animações mais completas. Mas se prepare pois elas também darão muito mais trabalho!

 

8.1 Roteiro e planejamento


No mundo da programação, programadores possuem a mania de querer começar a codificar antes de pensar no que eles precisam fazer. O mesmo vale aqui no Manim. Estamos escrevendo códigos para criar vídeos. É uma boa ideia planejar o que fazer antes de começar a programar. Para isso, é interessante criar um roteiro para a animação. Não precisa ser algo muito complexo ou bonito, mas que você entenda e que te ajude a criar as animações. É possível utilizar outros softwares para isso como:

Pode ser em qualquer forma, contanto que você entenda e que te ajude.

 

8.2 Editor de texto e ambiente de desenvolvimento


Como estamos usando código para produzir animações, apenas o bloco de notas não é o suficiente para produzir animações no Manim. É possível, mas nada eficiente. Para isso, existem as IDEs (Ambientes de Desenvolvimento Integrados). Eles são ferramentas que nos auxiliam a criar códigos. De certa forma, o Google Collab é uma IDE, mas com poucos recursos. Alguns dos recursos de IDEs que pdem ser citados são:

Também podemos usar editores de texto que não possuem todas essas ferramentas, mas é possível instalar plugins que dão acesso a essas ferramentas. Algumas das IDEs e editores de texto são:

 

8.3 Ambiente virtual


No Python, é interessante organizarmos as bibliotecas que usamos e não usar simplesmente instalá-las todas globalmente.
Para isso, usamos a virtuale onde instalamos todas as bibliotecas necessárias.

 

8.4 Estrutura do projeto


A organização em um projeto é muito importante. Organizar arquivos em diferentes pastas, cada uma com arquivos em comum. Ao renderizar coisas no Manim, elas ficam armazenadas dentro da pasta media. Damos o nome da nossa animação, por exemplo, geometria_analitica e colocamos um arquivo .py e um textos.txt onde armazenamos os textos usados na animação. E temos o ambiente virtua visto no último tópico

Obs: Essa parte de textos é opcional, mas serve para organizarmos o processo de criação da animação.

Desse modo, teremos uma estrutura parecida com a seguinte:

Com as coisas separadas, o projeto fica mais organizado.

 

8.5 Uso de comentários


Agora que abordamos a organização do projeto em pastas, vamos abordar a organização do código. Para isso, usaremos os comentários. Como já vimos, existem 2 tipos de comentários, os de uma linha e o de múltiplas linhas. Ns exemplos de códgos anteriores, comentamos cada linha para explicar o que estava acontecendo no código, apenas para fins didáticos, entretanto, essa não é uma forma interessante de usar os comentários. Devemos usá-los para anotar coisas de um modo mais geral. Por exemplo, no código de updaters

 

Usamos os comentários para separar dados, Mobjects e animações, e para documentar as funções.

 

8.6 Separando textos em outros arquivos


Ao criarmos textos no Manim, temos que dar nome a cada um deles. Para simplificar essa tarefa, podemos armazená-los em outro arquivo. É para isso que serve o textos.txt. Nele armazenamos os textos usados nas animações e quando precisarmos mudá-los, basta mudar no arquivo e ele mudará na animação. Podemos usar um código para pegar o conteúdo do arquivo e colocá-lo em uma lista onde estão todos os textos.

 

Criando o arquivo textos.txt:

 

Exemplo de animação com o uso do arquivo:

 

8.7 Generalização de cenas


Para a organização do código, é interessante dividir as partes em diferentes funções. Algumas dessas funções contém apenas uma cena estática, mas outras, é possível generalizar essa função. Por exemplo, se tivermos uma função animando um gráfico, podemos generalizar a função que será utilizada no gráfico.

 

8.8 Simplificando funções


Num código usando o Manim, frequentemente precisamos usar diversas linhas de código com a mesma forma. Por exemplo, um código pode ter diversas linhas com self.play(Write()). Em vez de repetir essas linhas ao longo do código principal, podemos simplificar todo o código usando funções lambda.

 

8.9 Criando seus próprios objetos


Os objetos do Manim são utilizados apenas para serem renderizadas no vídeo. Por exemplo, dados de uma elipse como o semi-eixo menor e o semi-eixo maior não são armazenados nos objetos do Manim. Podemos criá-los e herdar da classe Ellipse.

 

9. Finalizando com um exemplo prático


Chegamos ao fim deste pequeno manual sobre Manim/Python. Para o fim, deixaremos um exemplo de produção de uma animação. Primeiro, iremos apresentar a ideia da animação, ou seja, o que queremos mostrar com ela. Em seguida, iremos estruturar o projeto mostrando o roteiro com os passos a serem seguidos para se completar a animação e os objetos e os movimentos necessários. Por fim, apresentaremos o código do Manim que produz a animação.


Vamos supor que queremos animar o processo de se calcular o comprimento da diagonal de um quadrado de lado $a$. Um possível roteiro com as etapas da animação está mostrado a seguir. Entre colchetes [ ] estão os principais Mobjects e animações necessárias em cada etapa.

(1) A animação começaria com um quadrado aparecendo na tela. [Square, FadeIn]

(2) Depois disso, o valor do lado ($a$) seria mostrado próximo a dois lados consecutivos do quadrado. [MathTex,Write]

(3) Em seguida, a diagonal do quadrado apareceria na tela com uma cor diferente da do quadrado. [Line, Write, set_color]

(4) Próximo a ela, apareceria então o texto $x=?$. Esse texto vai indicar que queremos determinar o comprimento da diagonal. [MathTex, Write]

(5) O passo seguinte seria destacar o triângulo formado por dois lados consecutivos do quadrado e a diagonal, indicando que é um triângulo retângulo (mostrando que o ângulo entre os lados é reto). O destaque seria feito desenhando o triângulo com outra cor. [Polygon]

(6) Com a informação de que é um triângulo retângulo, seria feita a manipulação algébrica: $x^2 = a^2 + a^2 \rightarrow x^2 = 2a^2 \rightarrow x = \sqrt{2a^2} \rightarrow x = \sqrt{2}a$. [MathTex, TransformMatchingTex,ReplacementTransform]

(7) Após essa manipulação, a expressão $x = \sqrt{2}a$ é movida para a região próxima à diagonal. [MathTex, move_to]

 

Uma representação visual desse roteiro é apresentado na imagem a seguir (criada com o OneNote).

Imagem do roteiro

 

Definido o roteiro, primeiramente temos que pensar nos objetos que aparecem em mais de um passo. Isso porque vamos dividir os passos em funções no código. Como quase todos os objetos são utilizados por mais de um passo da cena, vamos defini-los todos como globais à cena, ou seja, usar o self em todos os objetos gráficos.

Vamos ao código. Primeiramente vamos definir o nome da cena, nomeando-a de DiagonalQuadrado. Também vamos declarar os métodos (funções de um objeto) dos passos da animação. Não precisa ser um método para cada passo, mas que tenhamos um certo número que deixe o código organizado. Também declaramos uma função para as figuras geométricas que aparecerão na cena. Com isso, teremos a seguinte estrutura do código:

class DiagonalQuadrado(Scene):
    def construct(self):
        # código da função

    def config_global(self):
        # código da função

    def mostrar_quadrado(self):
        # código da função

    def mostrar_triangulos(self):
        # código da função

    def mostrar_manipulacao_algebrica(self):
        # código da função

Agruparemos a definição e configuração dos objetos gráficos, textos, equações e funções auxiliares dentro da função config_global.


Quadrado:

self.quadrado = Square(side_length=tamanho_quadrado)\
  .set_color(cor_quadrado)\
  .move_to(posicao_quadrado)

Triângulo:

cantos_triangulo = (
  get_lados_quadrado(self.quadrado)[1],
  get_lados_quadrado(self.quadrado)[2],
  get_lados_quadrado(self.quadrado)[3],
)
self.triangulo = Polygon(*cantos_triangulo).set_color(cor_triangulo)

Quadrado com um ponto no meio para representar o símbolo do ângulo reto do triângulo retângulo:

ponto_angulo_retangulo = get_lados_quadrado(self.quadrado)[2]\
  + 0.5*tamanho_angulo_retangulo*UP\
  + 0.5*tamanho_angulo_retangulo*RIGHT
self.angulo_retangulo = VGroup(
  Square(side_length=tamanho_angulo_retangulo)\
  .move_to(ponto_angulo_retangulo)
  .set_color(cor_angulo_retangulo),
  Dot(ponto_angulo_retangulo).scale(0.15*tamanho_quadrado)
)

Diagonal do quadrado:

pontos_diagonal = (
  get_lados_quadrado(self.quadrado)[1],
  get_lados_quadrado(self.quadrado)[3]
)

self.diagonal = Line(*pontos_diagonal).set_color(cor_diagonal)

Agora as cores, tamanhos e posições dos objetos.

cor_quadrado = WHITE
  cor_lados_label = WHITE
  cor_diagonal = RED
  cor_x_label = cor_diagonal
  cor_triangulo = PURPLE
  cor_angulo_retangulo = BLUE

  tamanho_quadrado = 4
  tamanho_angulo_retangulo = tamanho_quadrado/10

  posicao_equacoes = 3*RIGHT
  posicao_quadrado = 2*LEFT

Definimos as variáveis de cor e posição para cada objeto para facilitar alterações caso necessário.


Textos e equações.

$a$:

self.lados_label = VGroup(
  MathTex('a').next_to(self.quadrado, direction=LEFT, buff=0.5),
  MathTex('a').next_to(self.quadrado, direction=DOWN, buff=0.5)
).set_color(cor_lados_label)

$x$:

self.x_label = MathTex('x=', '?').next_to(self.quadrado, direction=RIGHT, buff=0.5).set_color(cor_x_label)

Equações:

equacoes = [
  MathTex('x^2', '=', 'a^2', '+', 'a^2'),
  MathTex('x^2=2x^2'),
  MathTex('x=\sqrt{2a^2}'),
  MathTex('x=', '\sqrt{2}a')
]

self.equacoes = [
  equacao.move_to(posicao_equacoes) 
  for equacao in equacoes
]

Resultado:

self.resultado = MathTex('x=', '\sqrt{2}a')\
  .move_to(self.triangulo.get_center() + 0.5*UP + 0.8*RIGHT)\
  .set_color(cor_x_label)

Função auxiliar para obter as coordenadas dos vértices do quadrado:

def get_lados_quadrado(quadrado):
  lados_quadrado = [
    quadrado.point_from_proportion(i) 
    for i in np.arange(0, 1.25, 0.25)
  ]

  return lados_quadrado

Essa função é usada também para obter informações para a construção do triângulo e do quadrado que representa o ângulo reto:

def get_lados_quadrado(quadrado):
  lados_quadrado = [
      quadrado.point_from_proportion(i) 
      for i in np.arange(0, 1.25, 0.25)
  ]
  return lados_quadrado

 cantos_triangulo = (
    get_lados_quadrado(self.quadrado)[1],
    get_lados_quadrado(self.quadrado)[2],
    get_lados_quadrado(self.quadrado)[3],
)

ponto_angulo_retangulo = get_lados_quadrado(self.quadrado)[2]\
  + 0.5*tamanho_angulo_retangulo*UP\
  + 0.5*tamanho_angulo_retangulo*RIGHT
 

As funções que farão as animações dos objetos ficarão escritas como:

mostrar_quadrado()

def mostrar_quadrado(self):
  write = lambda *mobs: self.play(*(Write(mob) for mob in mobs))

  write(self.quadrado)
  write(self.lados_label)
  write(self.diagonal, self.x_label)

mostrar_triangulo()

def mostrar_triangulo(self):
  write = lambda *mobs: self.play(*(Write(mob) for mob in mobs))
  fadeout = lambda *mobs: self.play(FadeOut(*mobs))
  play = lambda *anim: self.play(*anim)

  write(self.triangulo)
  fadeout(self.quadrado)
  play(self.x_label.animate.move_to(self.triangulo.get_center() + 0.5*UP + 0.5*RIGHT))
  write(self.angulo_retangulo)

mostrar_manipulacao_algebrica()

def mostrar_manipulacao_algebrica(self):
  wait = lambda t=1: self.wait(t)
  play = lambda *anim, t=1: self.play(*anim, run_time=t)
  fadein = lambda *mobs: self.play(FadeIn(*mobs))

  play(
    ReplacementTransform(self.x_label.copy(), self.equacoes[0][0]),
    ReplacementTransform(self.lados_label[0].copy(), self.equacoes[0][2]),
    ReplacementTransform(self.lados_label[1].copy(), self.equacoes[0][4]),
    t=3
  )

  fadein(self.equacoes[0][1], self.equacoes[0][3])
  wait()
  play(TransformMatchingTex(self.equacoes[0], self.equacoes[1]), t=3)
  wait()
  play(TransformMatchingTex(self.equacoes[1], self.equacoes[2]), t=3)
  wait()
  play(TransformMatchingTex(self.equacoes[2], self.equacoes[3]), t=3)
  wait()

  play(
      TransformMatchingTex(self.x_label, self.resultado),
      ReplacementTransform(self.equacoes[3][1].copy(), self.resultado[1])
  )

  wait()
 

Agrupando tudo isso, teremos um possível código para produzir a animação desejada:

 

10. Consultando documentação


O Manim possui uma comunidade muito ativa atualmente que mantém o projeto e sua documentação. Para acessar essa documentação e redes sociais, acesse o seguinte link. Nele estão todas as formas de comunicação com a comunidade. Grande parte dos exemplos de animação criados neste manual foram criados consultando essa documentação.
Além da documentação oficial, há também outras fontes que podem ser consultadas. Uma delas é o canal de Alexander Vázquez Theorem of Beethoven e os materiais produzidos por ele disponível no Github.