Ir al contenido

¿Qué es un Mobject?

— Frankenstein

El mundo era para él un secreto que deseaba descifrar.

— Mary Shelley

Comenzamos con una de las partes fundamentales de Manim: los Mobjects.

Su nombre viene de Mathematical Objects o de Manim Objects, no queda del todo claro ni hay consenso. Se trata de clases que abstraen objetos que podemos representar en la pantalla.

Manim viene con un conjunto de Mobjects que podemos utilizar: Desde figuras geométricas a grafos, incluyendo texto e imágenes:

Código para generar este ejemplo
mobjects_examples.py
from manim import *
from pathlib import Path
class MobjectsExample(Scene):
def construct(self):
geometric_figure_example = Square()
vertices = [1, 2, 3, 4, 5, 6, 7, 8]
edges = [(1, 7), (1, 8), (2, 3), (2, 4), (2, 5),
(2, 8), (3, 4), (6, 1), (6, 2),
(6, 3), (7, 2), (7, 4)]
graph_example = Graph(
vertices,
edges,
layout="circular",
layout_scale=3,
labels={
v: Text(str(v), font_size=24, color=BLACK)
for v in vertices
},
vertex_config={
7: {"fill_color": RED}
},
edge_config={
(1, 7): {"stroke_color": RED},
(2, 7): {"stroke_color": RED},
(4, 7): {"stroke_color": RED}
}
).scale(0.3)
text_example = Text("Welcome to Manim!")
image_example = ImageMobject(Path("assets")/"python_example_logo.jpeg")
mobjects_group = Group(
geometric_figure_example,
graph_example,
text_example,
image_example
).arrange_in_grid()
self.add(mobjects_group)

Para poder ver un Mobject en nuestra Scene, debemos hacer dos cosas:

  1. Instanciar el objeto.
  2. Agregarlo a la escena.

En el ejemplo anterior hemos instanciado diferentes tipos de Mobject y los hemos agregado a la escena con el método add, el cual pertenece a la clase Scene. Hay más formas de agregar Mobjects a las escenas pero por el momento nos conformaremos con add.

add_square_example.py
from manim import *
class AddSquareExample(Scene):
def construct(self):
# Create instance of the object
square = Square()
# Add it to the Scene to make it visible.
self.add(square)

Los Mobjects tienen varios métodos y atributos que heredan de la clase Mobject y que podemos utilizar para manipular sus propiedades.

Cada tipo de Mobject tiene propiedades particulares, pero algunas de ellas son genéricas. Debido a que el objetivo es generar animaciones, Manim usa muy habitualmente el decorador property para definir getters y setters para esas propiedades, así como verbos específicos para poder modificar dicha propiedad. Algunas de sus propiedades más comunmente usadas y sus métodos asociados son:

  • Posición:
    • get_center: Devuelve un array con las coordenadas del centro del objeto.
    • set_x: Setea el valor de la coordenada x del objeto.
    • set_y: Setea el valor de la coordenada y del objeto.
    • set_z: Setea el valor de la coordenada z del objeto.
    • move_to: Setea las coordenadas x, y, z del objeto.
    • next_to: Setea las coordenadas x, y, z del objeto relativas a otro objeto.
    • to_edge: Setea las coordenadas x, y, z del objeto relativas a un borde de la escena.
    • to_corner: Setea las coordenadas x, y, z del objeto relativas a una esquina de la escena.
    • shift: Desplaza las coordenadas x, y, z del objeto.
  • Tamaño:
    • get_height: Devuelve la altura del objeto.
    • get_width: Devuelve la anchura del objeto.
    • get_left: Devuelve las coordenadas del punto más a la izquierda del objeto.
    • get_right: Devuelve las coordenadas del punto más a la derecha del objeto.
    • get_top: Devuelve las coordenadas del punto más alto del objeto.
    • get_bottom: Devuelve las coordenadas del punto más bajo del objeto.
    • scale: Escala el objeto proporcionalmente en todas direcciones.
    • stretch: Escala un objeto en proporción a un vector definido.
    • stretch_to_fit_width: Escala un objeto horizontalmente.
    • stretch_to_fit_height: Escala un objeto horizontalmente.
  • Rotación:
    • rotate: Aplica una rotación a partir de un ángulo, un eje y un punto fijo dados.
    • flip: Preset de rotate con un ángulo de 180º y aplicado a ejes en el plano.
    • rotate_about_origin. Similar a rotate pero usando el centro de la escena como punto fijo.

Aquí tenemos un ejemplo sencillo de cómo podemos aplicar algunas de estas modificaciones de las propiedades a un Triangle:

mobjects_common_properties.py
from manim import *
import random
class MobjectsCommonProperties(Scene):
def construct(self):
for i in range(25):
random.seed(i)
self.add(
Triangle()
.scale(random.randint(1, 20)/10) # Scale between 0.1 and 2
.shift(
[
random.randint(-60, 60)/10, # Shift x coordinate between -6 and 6
random.randint(-30, 30)/10, # Shift y coordinate between -3 and 3
0 # Keep z coordinate
]
)
.rotate(
random.randint(0, 360)*DEGREES # Rotate between 0 and 360 degrees
)
)

Los Mobjects pueden contener otros Mobjects. Esto es particularmente interesante a la hora de agrupar varios Mobjects en una sola unidad, a la hora de construir objetos compuestos o incluso a la hora de generar copias de un conjunto de objetos.

Podemos agregar submobjects usando el método add y podemos encontrar los submobjects de un Mobject usando la propiedad submobjects.

triforce.py
from manim import *
class TriforceScene(Scene):
def construct(self):
triangle_1 = Triangle()
triangle_2 = Triangle().shift(DOWN*triangle_1.height + LEFT*triangle_1.width/2)
triangle_3 = Triangle().shift(DOWN*triangle_1.height + RIGHT*triangle_1.width/2)
# We can use `add` to group Mobjects.
triangle_1.add(triangle_2, triangle_3)
triangle_1.move_to([-3, 0, 0])
# After grouping all the triangles,
# we can copy them and apply modifications.
triforce = triangle_1.copy().set_fill(opacity=1).set_color(GOLD).move_to([3, 0, 0])
self.add(
triangle_1, triforce
)
print(triforce.submobjects)

En la consola veremos el siguiente output:

Ventana de terminal
[Triangle, Triangle]

Esto es porque el triángulo triangle_1 actúa como el Mobject base y no se contabiliza a si mismo como parte de sus submobjects.

Dentro de los Mobjects tenemos varias familias. La más grande de ellas son los Vectorized Mobjects o VMobjects.

Ya hemos visto algunos ejemplos: Los cuadrados, los triángulo e incluso el texto son VMobjects.

Una imagen normal se puede definir como un array de pixels. Cada píxel es un cuadrado de un color determinado. Juntos conforman la imagen final.

Sin embargo, una imagen vectorial consiste en un conjunto de puntos unidos entre sí mediante curvas de Bézier.

Los VMobjects funcionan de forma similar a las imágenes vectoriales: Consisten en un conjunto de puntos unidos entre sí mediante curvas de Bézier.

Al tratarse de puntos y curvas, podemos crear infinidad de objetos que pueden ser escalados sin perder definición.

Habitualmente, las curvas de Bézier se definen en base a dos puntos y uno o varios tiradores. Gestionar todos y cada uno de los puntos y tiradores puede llegar a ser muy complicado. Por suerte, Manim hace gran parte del trabajo por nosotros.

Aquí tenemos un ejemplo de cómo podemos definir un cuadrado como 5 puntos unidos entre sí formando una figura cerrada. Si seteamos los puntos como esquinas, Manim automáticamente los unirá de forma secuencial. Podemos aplicar un suavizado a varios de esos puntos para obtener diferentes figuras.

manual_squares.py
from manim import *
class ManualSquares(Scene):
def construct(self):
manual_square = VMobject()
points = [
[1, 1, 0],
[1, -1, 0],
[-1, -1, 0],
[-1, 1, 0],
[1, 1, 0],
]
manual_square.set_points_as_corners(points)
smooth_square = VMobject().set_points_as_corners(points).make_smooth().shift(3*LEFT)
uneven_square = VMobject()
uneven_square.set_points_as_corners(points[0:3])
uneven_square.make_smooth()
uneven_square.add_points_as_corners(points[3:])
uneven_square.shift(3*RIGHT)
self.add(manual_square, smooth_square, uneven_square)

Agregando colorinchis a los VMobjects (y otras cosas chulas)

Sección titulada «Agregando colorinchis a los VMobjects (y otras cosas chulas)»

Otra ventaja que presentan los VMobjects es que podemos agregarles colores. Manim distingue entre el borde y el relleno, cuyas propiedades podemos modificar por separado. Por ejemplo, podemos modificar el grosor del borde, la opacidad e incluso agregar gradientes de colores.

color_circles.py
from manim import *
class ColorCircles(Scene):
def construct(self):
c1 = Circle(color=GREEN)
c2 = Circle(color=GREEN, fill_opacity=1)
c3 = Circle(stroke_color=RED, fill_color=BLUE, fill_opacity=1)
c4 = Circle(stroke_color=RED, fill_color=BLUE, fill_opacity=0.5)
c5 = Circle(stroke_color=PURPLE, fill_color=YELLOW, stroke_opacity=1, fill_opacity=1, stroke_width=20)
c6 = Circle(stroke_color=PURPLE, fill_color=YELLOW, stroke_opacity=0.5, fill_opacity=1, stroke_width=20)
c7 = Circle(fill_color=color_gradient([RED, GREEN, BLUE, PURPLE, YELLOW], 5), fill_opacity=1)
c8 = Circle(fill_color=color_gradient([RED, GREEN, BLUE, PURPLE, YELLOW], 5), fill_opacity=1, sheen_direction=UP)
circles = Group(c1,c2,c3,c4,c5,c6,c7,c8).arrange_in_grid()
rectangle = Rectangle(width=circles.width*1.2, height=circles.height*1.2, fill_opacity=1, color=TEAL)
self.add(rectangle, circles)

Para un listado completo de los colores que ofrece Manim vista la documentación.

Ahora que ya sabemos cómo usar los Mobjects, podemos comenzar con las distintas implementaciones que Manim trae por defecto.