Mobjects personalizados
Hasta ahora hemos visto como podemos usar los objetos que vienen por defecto en Manim para hacer animaciones. Pero… ¿Qué pasa si quiero hacer objetos personalizados o a mi medida?
Aquí entra en juego la herencia en python. Podemos crear objetos nuevos heredando de Mobject, de VMobject, de VGroup o de cualquier otra clase de Mobject que queramos.
Para ello, es tan sencillo como implementar una subclase y modificar lo que queramos.
Personalizando Mobjects existentes
Sección titulada «Personalizando Mobjects existentes»Supongamos que queremos utilizar la clase Dot pero es muy chiquita para nuestro caso. Podemos implementar una versión más grande fácilmente:

from manim import *
class CustomDot(Dot): def __init__(self, point=ORIGIN, radius=3, **kwargs): super().__init__(point, radius, **kwargs)
class CustomDotExample(Scene): def construct(self): dots = VGroup( Dot(), CustomDot(color=RED) ).arrange()
self.add(dots)Creando Mobjects de cero
Sección titulada «Creando Mobjects de cero»Modificar cosas que ya existen puede estar bien, pero la gracia de saber programar es poder crear lo que nosotros queramos. Esto lo podemos hacer heredando de las clases Mobject o VMobject.
Aquí, por ejemplo, haremos una clase que dibuje una espiral. Para ello, vamos a heredar de VMobject y vamos a sobreescribir el método _generate_points.

from manim import *import numpy as np
class Spiral(VMobject): def __init__( self, start_radius: float = 0.0, end_radius: float = 3.0, num_turns: float = 3.0, start_angle: float = 0.0, direction: int = 1, num_samples: int = 400, **kwargs, ): super().__init__(**kwargs) self.start_radius = start_radius self.end_radius = end_radius self.num_turns = num_turns self.start_angle = start_angle self.direction = direction self.num_samples = num_samples
self._generate_points()
def _radius_at(self, t: float) -> float: """Return the radius at normalized parameter t in [0, 1].""" a, b = self.start_radius, self.end_radius
return a + (b - a) * t
def _generate_points(self): total_angle = self.direction * self.num_turns * 2 * PI ts = np.linspace(0.0, 1.0, self.num_samples)
points = [] for t in ts: theta = self.start_angle + total_angle * t r = self._radius_at(t) x = r * np.cos(theta) y = r * np.sin(theta) points.append([x, y, 0.0])
self.set_points_as_corners( [ORIGIN] + points if self.start_radius == 0 else points) self.make_smooth()
def get_num_turns(self) -> float: return self.num_turns
def get_end_point(self) -> np.ndarray: return self.points[-1]
class SpiralExample(Scene): def construct(self):
spiral = Spiral( start_radius=0.2, end_radius=2.2, num_turns=4, color=BLUE, stroke_width=3, )
self.add(spiral)Componiendo objetos con VGroup
Sección titulada «Componiendo objetos con VGroup»Podemos heredar de VGroup para generar objetos compuestos en lugar de ir definiendo todos sus puntos. Una gran ventaja de esto es que podemos aprovechar los VMobjects ya existentes y utilizarlos para simplificarnos el trabajo.

from manim import *
class Element(VGroup): def __init__(self, symbol="H", element="Hydrogen", atomic_number=1, bg_color=BLUE_A, text_color=BLACK, height=3, widht=2, **kwargs): self.background = Rectangle(height=height, width=widht, fill_opacity=1, color=bg_color) self.symbol = Text(symbol, color=text_color).scale_to_fit_height(self.background.height * 0.35).move_to(self.background.get_center()) self.element = Text(element, color=text_color).scale_to_fit_width(self.background.width*0.75).next_to(self.symbol, DOWN) self.atomic_number = Text(str(atomic_number), color=text_color).scale_to_fit_height(0.1 * self.background.height).next_to(self.symbol, UL) self.bg_color = bg_color self.text_color = text_color vmobjects = [ self.background, self.element, self.symbol, self.atomic_number ] super().__init__(*vmobjects, **kwargs)
class ElementExample(Scene): def construct(self): hydrogen = Element() carbon = Element("C", "Carbon", 6) oxygen = Element("O", "Oxygen", 8) nitrogen = Element("N", "Nitrogen", 7) elements = VGroup( hydrogen, carbon, oxygen, nitrogen ).arrange() self.add(elements)¡Ejercicios!
Sección titulada «¡Ejercicios!»Ejercicio 1
Sección titulada «Ejercicio 1»Crea una figura con un punto central del cuál brotan un número indeterminado de brazos como si fuera una galaxia espiral.
Solución al ejercicio 1

from manim import *import numpy as np
class SpiralGalaxy(VMobject): def __init__( self, arms=5, turns=1.5, max_radius=3.0, curvature=0.22, color=BLUE_B, **kwargs, ): self.arms = arms self.turns = turns self.max_radius = max_radius self.curvature = curvature self.arms_color = color
super().__init__(**kwargs)
def generate_points(self): for i in range(self.arms): offset = TAU * i / self.arms arm = self._build_arm(offset)
self.append_points(arm.points)
def _build_arm(self, offset): sample_points = 80 pts = [] for k in range(sample_points): t = k / (sample_points - 1) theta = offset + t * self.turns * TAU r = self.max_radius * ( np.exp(self.curvature * (theta - offset)) - 1 ) / (np.exp(self.curvature * self.turns * TAU) - 1) pts.append([r * np.cos(theta), r * np.sin(theta), 0.0])
arm = VMobject() arm.set_points_smoothly(pts) return arm
def init_colors(self): super().init_colors() self.set_color(self.arms_color)
class CustomObjectsExampleOne(Scene): def construct(self): self.add(SpiralGalaxy())Ejercicio 2
Sección titulada «Ejercicio 2»Crea una figura que contenga un texto encuadrado en un rectángulo. Se deben poder configurar el texto, el color del texto, el color del rectángulo y el color del borde del rectángulo.
Solución al ejercicio 2

from manim import *import numpy as np
class BoxedText(VGroup): def __init__(self, text="", text_color=BLACK, box_color=BLUE, box_stroke_color=BLUE_E, **kwargs): VERTIICAL_MARGIN = 1.5 HORIZONTAL_MARGIN = 1.2 self.text = Text(text, color=text_color) self.text_color = text_color self.box = Rectangle( height=VERTIICAL_MARGIN*self.text.height, width=HORIZONTAL_MARGIN*self.text.width, fill_opacity=1, fill_color=box_color, stroke_color=box_stroke_color ) self.box_color = box_color self.box_stroke_color = box_stroke_color
vmobjects = [self.box, self.text] super().__init__(*vmobjects, **kwargs)
class CustomObjectsExampleTwo(Scene): def construct(self): self.add(BoxedText("Hello World!"))