I am trying to write an animation, where number is recalculated, based on the values of t1, t2
, which are valueTrackers
. Precisely, equation3
, which is a result of predefined function get_integral
, that computes the definite integral of a function with given lower and upper bounds:
equation3 = MathTex(
"{:.3f}".format(self.get_integral(t2.get_value(), t1.get_value()))
)
Over the span of 5 seconds I want to see the update of equation3
many times, as values t1 and t2 are getting closer to 0.5
and 0.6
correspondingly from some initial values.
self.play(
t1.animate.set_value(0.5),
t2.animate.set_value(0.6),
UpdateFromFunc(equation3, lambda m: m.set_value("{:.3f}".format(
self.get_integral(t2.get_value(), t1.get_value())
))),
run_time=5
)
With the current code, value equation 3
stays the same throughout the whole 5 seconds.
Not clear what you expected with m.set_value()
here. In this context m
is equation3
, which is of type MathTex
, and that class does not have a .set_value()
method.
It does not produce errors, because when Manim has to run a object.set_whatever(n)
function and that function is not defined, it captures that call and performs a object.whatever=n
. So in your particular case you are storing a number in equation3.value
. But, since this property is not used by manim, it has no effect on the aspect of the rendered object.
But even if you replace the attribute in which MathTex
stores the text to be rendered, that doesn't mean that the object will be re-rendered after the change. This is true in general for any manim object, but it is easier to understand in the case of Tex related objects. It is not enough to change the text stored in the object, manim has to call latex to convert that text into a graphic object, and it will not do that unless some updater makes it to do that.
In essence, in the lambda of your UpdateFromFunc()
, changing attributes of objects is not enough. You need to trigger the re-render of the object after it was changed. One way of doing so is through mobject.become()
. You provide a new mobject as parameter, and the old one gets replaced by the new one.
A simple example: (I don't have your get_integral()
method, so I provide a dummy one)
class Test(Scene):
def get_integral(self, t2, t1):
return t2*t1
def construct(self):
t1 = ValueTracker(0)
t2 = ValueTracker(0)
equation3 = MathTex(
"{:.3f}".format(self.get_integral(t2.get_value(), t1.get_value()))
)
self.play(
t1.animate.set_value(0.5),
t2.animate.set_value(0.6),
UpdateFromFunc(equation3, lambda m: m.become(MathTex("{:.3f}".format(
self.get_integral(t2.get_value(), t1.get_value())
)))),
run_time=5
)
Using this approach the result is:
The usual approach in this case would be to use always_redraw()
, like this:
class Test(Scene):
def get_integral(self, t2, t1):
return t2*t1
def construct(self):
t1 = ValueTracker(0)
t2 = ValueTracker(0)
equation3 = always_redraw(lambda: MathTex(
"{:.3f}".format(self.get_integral(t2.get_value(), t1.get_value()))
))
self.add(equation3)
self.play(
t1.animate.set_value(0.5),
t2.animate.set_value(0.6),
run_time=5
)
This function basically adds an updater to equation3
which contains a self.become()
which takes as argument the result of calling the lambda. That updater is called at each frame, so the object automatically "redraws" itself at each frame with the result of the provided lambda.