Updaters: Things That Follow
An updater keeps one thing in step with another: a label that follows a dot, a line that always joins two points, a number that counts up as something moves.
A label that follows
from pymations import *
dot = Dot(LEFT * 4, color=YELLOW)
label = Text("me", font_size=32)
label.add_updater(lambda m: m.next_to(dot, UP))
add(dot, label)
play(dot.animate.shift(RIGHT * 8), run_time=3)
play(dot.animate.shift(DOWN * 2))
m.add_updater(function) runs function(m) on every frame, so whatever it does to m is redone as things move. Here the label is put above the dot on every frame.
always(label.next_to, dot, UP) is a shorter way to write the same thing.
A shape that is rebuilt
always_redraw makes a new shape on every frame from a function:
from pymations import *
a = Dot(LEFT * 3, color=BLUE)
b = Dot(RIGHT * 3, color=ORANGE)
line = always_redraw(lambda: Line(a.get_center(), b.get_center()))
add(line, a, b)
play(a.animate.shift(UP * 2), b.animate.shift(DOWN * 2), run_time=2)
play(Rotate(VGroup(a, b), angle=PI), run_time=2)
A number to animate: ValueTracker
A ValueTracker holds a number that you can animate. Other things follow it with updaters:
from pymations import *
x = ValueTracker(0)
axes = Axes(x_range=[0, 6, 1], y_range=[-1.5, 1.5, 1], x_length=10, y_length=4, tips=False)
curve = axes.plot(lambda t: np.sin(t), color=BLUE)
dot = always_redraw(lambda: Dot(axes.c2p(x.get_value(), np.sin(x.get_value())), color=YELLOW))
add(axes, curve, dot)
play(x.animate.set_value(6), run_time=4, rate_func=linear)
play(x.animate.set_value(PI / 2))
| Tracker | What it does |
|---|---|
ValueTracker(0) | A number, starting at 0 |
t.get_value() | Its value (inside an updater, the value on that frame) |
t.animate.set_value(5) | Plays it changing to 5 |
t.animate.increment_value(1) | Plays it going up by 1 |
t.set_value(5) | Sets it at once |
Variable(0, "x") shows x = 0.00 with a tracker of its own, var.tracker: animate the tracker and the number counts.
Steady movement
An updater that takes a second argument, dt, is given the time since the last frame. Multiply a speed by it for steady movement:
from pymations import *
square = Square(color=TEAL)
square.add_updater(lambda m, dt: m.rotate(dt))
add(square)
wait(4)
always_rotate(m) and always_shift(m, RIGHT) are ready-made versions.
Trails
TracedPath(dot.get_center) draws the path a point leaves behind as it moves:
from pymations import *
dot = Dot(RIGHT * 2, color=YELLOW)
trail = TracedPath(dot.get_center, stroke_color=YELLOW, stroke_width=3)
add(trail, dot)
play(Rotate(dot, angle=TAU, about_point=ORIGIN), run_time=3, rate_func=linear)
Stopping an updater
m.remove_updater(function) takes one off, m.clear_updaters() takes them all off, and m.suspend_updating() / m.resume_updating() pause and restart them.
Scrubbing and recording still work
Pymations reads an updater once, when the file runs, and turns it into a rule the preview can work out for any moment. So scrubbing back and forth shows the right picture, and a recorded video is exact.
A few updaters can't be read that way: one with an if that depends on a moving value, or one that uses math.sin and the other math functions. Those run in Python on every frame instead. They still work, but the program stays running while the preview plays, and the output under the preview prints a note naming the line.
Use np.sin, np.cos, np.sqrt and the other np functions in updaters instead of math. They keep the updater in the preview, where it is faster and scrubs back and forth exactly:
from pymations import *
t = ValueTracker(0)
dot = Dot(color=YELLOW)
dot.add_updater(lambda m: m.move_to([2 * np.cos(t.get_value()), 2 * np.sin(t.get_value()), 0]))
add(dot)
play(t.animate.set_value(TAU), run_time=3, rate_func=linear)
Related
- Animations
- Graphs
- Mixing with Pyctures: driving a tracker with the mouse