"""Simple mobjects that can be used for storing (and updating) a value."""
from __future__ import annotations
__all__ = ["ValueTracker", "ComplexValueTracker"]
import numpy as np
from manim.mobject.mobject import Mobject
from manim.mobject.opengl.opengl_compatibility import ConvertToOpenGL
from manim.utils.paths import straight_path
[docs]class ValueTracker(Mobject, metaclass=ConvertToOpenGL):
"""A mobject that can be used for tracking (real-valued) parameters.
Useful for animating parameter changes.
Not meant to be displayed. Instead the position encodes some
number, often one which another animation or continual_animation
uses for its update function, and by treating it as a mobject it can
still be animated and manipulated just like anything else.
This value changes continuously when animated using the :attr:`animate` syntax.
Examples
--------
.. manim:: ValueTrackerExample
class ValueTrackerExample(Scene):
def construct(self):
number_line = NumberLine()
pointer = Vector(DOWN)
label = MathTex("x").add_updater(lambda m: m.next_to(pointer, UP))
tracker = ValueTracker(0)
pointer.add_updater(
lambda m: m.next_to(
number_line.n2p(tracker.get_value()),
UP
)
)
self.add(number_line, pointer,label)
tracker += 1.5
self.wait(1)
tracker -= 4
self.wait(0.5)
self.play(tracker.animate.set_value(5)),
self.wait(0.5)
self.play(tracker.animate.set_value(3))
self.play(tracker.animate.increment_value(-2))
self.wait(0.5)
.. note::
You can also link ValueTrackers to updaters. In this case, you have to make sure that the
ValueTracker is added to the scene by ``add``
.. manim:: ValueTrackerExample
class ValueTrackerExample(Scene):
def construct(self):
tracker = ValueTracker(0)
label = Dot(radius=3).add_updater(lambda x : x.set_x(tracker.get_value()))
self.add(label)
self.add(tracker)
tracker.add_updater(lambda mobject, dt: mobject.increment_value(dt))
self.wait(2)
"""
def __init__(self, value=0, **kwargs):
super().__init__(**kwargs)
self.set_points(np.zeros((1, 3)))
self.set_value(value)
[docs] def get_value(self) -> float:
"""Get the current value of this ValueTracker."""
return self.points[0, 0]
[docs] def set_value(self, value: float):
"""Sets a new scalar value to the ValueTracker"""
self.points[0, 0] = value
return self
[docs] def increment_value(self, d_value: float):
"""Increments (adds) a scalar value to the ValueTracker"""
self.set_value(self.get_value() + d_value)
return self
def __bool__(self):
"""Return whether the value of this value tracker evaluates as true."""
return bool(self.get_value())
def __iadd__(self, d_value: float):
"""adds ``+=`` syntax to increment the value of the ValueTracker"""
self.increment_value(d_value)
return self
def __ifloordiv__(self, d_value: float):
"""Set the value of this value tracker to the floor division of the current value by ``d_value``."""
self.set_value(self.get_value() // d_value)
return self
def __imod__(self, d_value: float):
"""Set the value of this value tracker to the current value modulo ``d_value``."""
self.set_value(self.get_value() % d_value)
return self
def __imul__(self, d_value: float):
"""Set the value of this value tracker to the product of the current value and ``d_value``."""
self.set_value(self.get_value() * d_value)
return self
def __ipow__(self, d_value: float):
"""Set the value of this value tracker to the current value raised to the power of ``d_value``."""
self.set_value(self.get_value() ** d_value)
return self
def __isub__(self, d_value: float):
"""adds ``-=`` syntax to decrement the value of the ValueTracker"""
self.increment_value(-d_value)
return self
def __itruediv__(self, d_value: float):
"""Sets the value of this value tracker to the current value divided by ``d_value``."""
self.set_value(self.get_value() / d_value)
return self
[docs] def interpolate(self, mobject1, mobject2, alpha, path_func=straight_path()):
"""
Turns self into an interpolation between mobject1
and mobject2.
"""
self.set_points(path_func(mobject1.points, mobject2.points, alpha))
return self
[docs]class ComplexValueTracker(ValueTracker):
"""Tracks a complex-valued parameter.
When the value is set through :attr:`animate`, the value will take a straight path from the
source point to the destination point.
Examples
--------
.. manim:: ComplexValueTrackerExample
class ComplexValueTrackerExample(Scene):
def construct(self):
tracker = ComplexValueTracker(-2+1j)
dot = Dot().add_updater(
lambda x: x.move_to(tracker.points)
)
self.add(NumberPlane(), dot)
self.play(tracker.animate.set_value(3+2j))
self.play(tracker.animate.set_value(tracker.get_value() * 1j))
self.play(tracker.animate.set_value(tracker.get_value() - 2j))
self.play(tracker.animate.set_value(tracker.get_value() / (-2 + 3j)))
"""
[docs] def get_value(self):
"""Get the current value of this value tracker as a complex number.
The value is internally stored as a points array [a, b, 0]. This can be accessed directly
to represent the value geometrically, see the usage example."""
return complex(*self.points[0, :2])
[docs] def set_value(self, z):
"""Sets a new complex value to the ComplexValueTracker"""
z = complex(z)
self.points[0, :2] = (z.real, z.imag)
return self