.. role:: underbold
:class: underbold
Basic Updaters
======================
Basic theory
-----------------
Updaters are **functions** that are applied to a Mobject and that are updated every frame.
.. raw:: html
:underbold:`Question`: How would we make sure that the square is always above the circle without having to manipulate the square?
Basically, we need the ``square.next_to(circle,UP)`` to apply each frame of the animation, for this we can use the Updaters.
.. raw:: html
If the functions are simple then we can use anonymous functions to make it shorter.
.. raw:: html
And this updater is going to run until we pause and delete it.
* Pause updaters: ``Mobject.suspend_updating()``
* Restore updaters: ``Mobject.resume_updating()``
* Delete all updaters: ``Mobject.clear_updaters()``
Yes, you can add more updaters, each Mobject has an attribute (a list) where it stores its updaters, so you can add or remove more updaters.
``.become``
----------------
Now imagine that we want the ``Brace`` to change in size depending on the line.
.. raw:: html
There are some Mobjects that you can change their shape through their methods, but the most general way is to use ``.become``.
The easiest way to understand ``.become`` is by making the analogy with ``Transform``, basically ``.become`` is an instant ``Transform``, and I can demonstrate it with the following code.
.. code-block:: python
def construct(self):
c = Circle().scale(2)
s = Square().scale(2)
c.generate_target()
c.target.become(s) # <-- it's an instant transformation
self.add(c)
self.play(MoveToTarget(c))
self.wait()
.. raw:: html
Show result
Using this concept, we can instantly transform a Mobject each frame, so it will give the feeling that the transformation is smooth.
.. code-block:: python
def construct(self):
line = Line(LEFT,RIGHT)
def get_brace(mob):
mob.become(
Brace(line,UP)
)
brace = VMobject() # This is the initial status,
# when we add the updater it will update
brace.add_updater(get_brace)
self.add(line,brace)
self.wait(0.5)
self.play(line.animate.scale(3))
self.wait(0.5)
self.play(line.animate.scale(0.8))
self.wait(0.5)
self.play(line.animate.scale(1.3))
self.wait(0.5)
.. raw:: html
Show result
Some Mobjects already have methods that help to deform them, for example, lines and vectors have the ``put_start_and_end_on`` method:
.. code-block:: python
def construct(self):
start_dot = Dot(LEFT,color=RED)
end_dot = Dot(RIGHT,color=TEAL)
def update_line(mob):
mob.put_start_and_end_on(start_dot.get_center(),end_dot.get_center())
line = Line() # Also it works with Arrow
line.add_updater(update_line)
self.add(line, start_dot, end_dot)
self.wait(0.5)
self.play(start_dot.animate.shift(LEFT*2+UP))
self.wait(0.5)
self.play(end_dot.animate.shift(RIGHT+DOWN*0.5))
self.wait(0.5)
self.play(
start_dot.animate.shift(RIGHT*0.5+DOWN*2),
end_dot.animate.shift(RIGHT+UP*3).scale(5)
)
self.wait(0.5)
self.play(
start_dot.animate.shift(LEFT*3).scale(5),
end_dot.animate.shift(DOWN*2)
)
self.wait(0.5)
.. raw:: html
Show result
ValueTracker
-----------------
This class does not appear on the screen, it is simply a tool that allows us to increase or decrease numerical values.
.. code-block:: python
def construct(self):
nl = NumberLine(include_numbers=True)
selector = Triangle(fill_opacity=1).scale(0.2).rotate(PI/3)
vt = ValueTracker(0) # <-- needs a start value
def update_selector(mob):
mob.next_to(nl.n2p(vt.get_value()),UP,buff=0)
# --------------
# In this way we can acces to the ValueTracker value
selector.add_updater(update_selector)
self.add(nl,selector)
self.wait(0.5)
self.play(
vt.animate.set_value(4),
run_time=2
)
self.wait(0.5)
self.play(
vt.animate.set_value(-1),
run_time=2
)
self.wait(0.5)
self.play(
vt.animate.set_value(-7),
run_time=2
)
self.wait(0.5)
self.play(
vt.animate.set_value(7),
run_time=3
)
self.wait(0.5)
.. raw:: html
Show result
As an exercise try to duplicate this animation:
.. raw:: html
DecimalNumber
-----------------
Now we will understand the value of ``DecimalNumber``, which is similar to ``ValueTracker``, only that it can be displayed on the screen:
.. code-block:: python
def construct(self):
nl = NumberLine(include_numbers=True)
selector = Triangle(fill_opacity=1).scale(0.2).rotate(PI/3)
dn = DecimalNumber(0) # <-- needs a start value
def update_selector(mob):
mob.next_to(nl.n2p(dn.get_value()),UP,buff=0)
# --------------
# In this way we can acces to the DecimalNumber value
def update_dn(mob):
mob.next_to(selector,UP,buff=0.1)
selector.add_updater(update_selector)
dn.add_updater(update_dn)
self.add(nl,selector,dn)
self.wait(0.5)
self.play(
ChangeDecimalToValue(dn, 4),
run_time=2
)
self.wait(0.5)
self.play(
ChangeDecimalToValue(dn, -1),
run_time=2
)
self.wait(0.5)
self.play(
ChangeDecimalToValue(dn, -4),
run_time=2
)
self.wait(0.5)
self.play(
ChangeDecimalToValue(dn, 7),
run_time=3
)
self.wait(0.5)
.. raw:: html
Show result
Group Updaters
-----------------
Sometimes it is easier for us to create an updater that modifies several Mobjects, this can be done using Groups.
.. code-block:: python
def construct(self):
nl = NumberLine(include_numbers=True)
selector = Triangle(fill_opacity=1,color=RED).scale(0.2).rotate(PI/3)
dn = DecimalNumber(0)
update_grp = VGroup(selector, dn)
def update_vgrp(vgrp):
s,d = vgrp
s.next_to(nl.n2p(dn.get_value()),UP,buff=0)
d.next_to(selector,UP,buff=0.1)
update_grp.add_updater(update_vgrp)
self.add(nl,update_grp) # <-- add grp complete, not each element
# Not use self.add(nl, selecttor, dn)
self.wait(0.5)
self.play(
ChangeDecimalToValue(dn, 4),
run_time=2
)
self.wait(0.5)
self.play(
ChangeDecimalToValue(dn, -1),
run_time=2
)
self.wait(0.5)
self.play(
ChangeDecimalToValue(dn, -4),
run_time=2
)
self.wait(0.5)
self.play(
ChangeDecimalToValue(dn, 7),
run_time=3
)
self.wait(0.5)
.. raw:: html
Show result