Trackers y Updaters
En ejemplos anteriores hemos visto cómo usar updaters para hacer animaciones.
from manim import *import numpy as npfrom math import cos, sin
class RosePattern(VMobject): def __init__(self, radius=2, k=3, **kwargs): super().__init__(**kwargs) self.radius = radius self.k = k
step_size = 0.05 theta = np.arange(0, TAU + step_size, step_size)
points = [ [ radius * cos(k*t) * cos(t), radius * cos(k*t) * sin(t), 0 ] for t in theta ]
self.set_points_smoothly(points)
class RosePatternExample(Scene): def construct(self):
def update_pattern(pat, dt): pat.k += dt new_pat = RosePattern(k=pat.k, color=MAROON_A) pat.become(new_pat)
rose = RosePattern(k=0) self.add(rose) rose.add_updater(update_pattern)
self.wait(10) rose.remove_updater(update_pattern) self.wait()Ahora vamos a entender mejor cómo funcionan y cómo los podemos utilizar para hacer animaciones.
Updaters
Sección titulada «Updaters»Un updater no es más que una función que le dice a un objeto qué valor debe tener una propiedad suya en función de la propiedad de otro objeto o parámetro.
En el ejemplo anterior, vemos cómo el updater depende del tiempo, de ahí que use dt, siendo la propiedad k la que modificamos. Es cierto que es un ejemplo un poco exagerado, ya que en cada unidad de tiempo estamos creando una nueva instancia de RosePattern pero no siempre tiene que ser así.
Por ejemplo, podemos hacer que un texto cambie en función de las propiedades de otro objeto.
from manim import *
class TextUpdaterExample(Scene): def construct(self): red_text = DecimalNumber(color=RED, num_decimal_places=2, mob_class=Text) green_text = DecimalNumber(color=GREEN, num_decimal_places=2, mob_class=Text) blue_text = DecimalNumber(color=BLUE, num_decimal_places=2, mob_class=Text) texts = VGroup(red_text, green_text, blue_text).arrange(RIGHT, buff=2).to_edge(UP) circle = Circle(fill_opacity=1, color=RED)
def red_color_updater(mob: DecimalNumber): mob.set_value(circle.fill_color.to_rgb()[0])
def green_color_updater(mob: DecimalNumber): mob.set_value(circle.fill_color.to_rgb()[1])
def blue_color_updater(mob: DecimalNumber): mob.set_value(circle.fill_color.to_rgb()[2])
red_text.add_updater(red_color_updater) green_text.add_updater(green_color_updater) blue_text.add_updater(blue_color_updater)
self.add(texts, circle) self.wait() self.play(circle.animate.set_color(GREEN)) self.play(circle.animate.set_color(BLUE)) self.play(circle.animate.set_color(RED))Al igual que con el ejemplo de la rosa, los updaters pueden depender del tiempo, de forma que podemos tener un objeto siendo modificado constantemente independientemente de qué más estemos haciendo en ese momento. El objeto seguirá el updater hasta que se lo quitemos con remove_updater.
from manim import *
class ConstantUpdaterExample(Scene): def construct(self): sq = Square().scale(2)
def rotate_updater(mob, dt): mob.rotate(dt)
self.add(sq)
self.wait() sq.add_updater(rotate_updater) self.wait(2) self.play(sq.animate.shift(UP)) self.play(sq.animate.shift(DOWN)) self.wait() sq.remove_updater(rotate_updater) self.wait(2)Trackers
Sección titulada «Trackers»Los ValueTrackers son objetos cuya única función es la de establecer un valor determinado para que un updater pueda seguirlos. Así, en lugar de modificar las propiedades de un Mobject que pueda estar en pantalla o que no nos interese, podemos simplemente modificar el valor del ValueTracker. El valor del tracker se puede cambiar de golpe o puede aniamrse como una propiedad más.
from manim import *
class TangentAnimation(Scene): def construct(self): ax = Axes() sine = ax.plot(np.sin, color=RED) alpha = ValueTracker(0) point = always_redraw( lambda: Dot( sine.point_from_proportion(alpha.get_value()), color=BLUE ) ) tangent = always_redraw( lambda: TangentLine( sine, alpha=alpha.get_value(), color=YELLOW, length=4 ) ) self.add(ax, sine, point, tangent) self.play(alpha.animate.set_value(1), rate_func=linear, run_time=2)¡Ejercicios!
Sección titulada «¡Ejercicios!»Ejercicio 1
Sección titulada «Ejercicio 1»Haz una animación en la que un punto se mueva por la escena de forma que su posición en el eje x dependa de un ValueTracker. Al lado del punto, debe haber un DecimalNumber que indique el valor de ese ValueTracker constantemente.
Solución al ejercicio 1
from manim import *
class TrackerExerciseOne(Scene): def construct(self): dot = Dot() number = DecimalNumber(mob_class=Text).next_to(dot, UP) tracker = ValueTracker(0)
def dot_updater(mob: Mobject): mob.move_to([tracker.get_value(), mob.get_y(), 0])
def number_updater(mob: DecimalNumber): mob.next_to(dot, UP) mob.set_value(dot.get_x())
dot.add_updater(dot_updater) number.add_updater(number_updater)
self.add(dot, number) self.wait() self.play(tracker.animate.set_value(-5), rate_func=there_and_back, run_time=4) self.play(tracker.animate.set_value(5), rate_func=there_and_back, run_time=4) self.wait()