ManimCommunity / manim

A community-maintained Python framework for creating mathematical animations.
https://www.manim.community
MIT License
26.64k stars 1.82k forks source link

Manim 0.18.1: Updaters don't work with objects which have been added using LaggedStart() #3950

Open uwezi opened 1 month ago

uwezi commented 1 month ago

Description of bug / unexpected behavior

When adding an object to a scene using LaggedStart() no updaters are recognized:

Expected behavior

well, updaters should be executed... Especially when added afterwards

How to reproduce the issue

Code for reproducing the problem ```py class laggingUpdater(Scene): def construct(self): vt = ValueTracker(0) dot1a = Dot().shift(3*UP) dot2a = Dot().shift(2*UP) dot3a = always_redraw(lambda: Dot().shift(1*UP+vt.get_value()*RIGHT) ) dot1b = Dot().shift(1*DOWN) dot2b = Dot().shift(2*DOWN) dot3b = always_redraw(lambda: Dot().shift(3*DOWN+vt.get_value()*RIGHT) ) def updater(mobj): mobj.set_x(vt.get_value()) dot1a.add_updater(updater) dot1b.add_updater(updater) self.play( LaggedStart( Create(dot1a), Create(dot2a), Create(dot3a) ) ) self.play( Create(dot1b), Create(dot2b), Create(dot3b) ) dot2a.add_updater(updater) dot2b.add_updater(updater) self.wait() self.play(vt.animate.set_value(7),run_time=4) self.wait() ```

Additional media files

Rendered video https://github.com/user-attachments/assets/4faf7933-f0b9-4786-ab69-8b66cba20420

System specifications

System Details - OS: Windows 10 - RAM: enough - Python version: 3.11.6 - Manim v0.18.1 PASTE HERE ```

Additional comments

https://discord.com/channels/581738731934056449/1293588131999907851/1293588131999907851

uwezi commented 1 month ago

The problem is caused by the fact that updating is suspended during the LaggedStart() animation, but it is not resumed automatically afterwards.

class laggingUpdater(Scene):
    def construct(self):
        vt = ValueTracker(0)
        dot1a = Dot().shift(3*UP)
        dot2a = Dot().shift(2*UP)
        dot3a = always_redraw(lambda:
            Dot().shift(1*UP+vt.get_value()*RIGHT)
        )
        dot1b = Dot().shift(1*DOWN)
        dot2b = Dot().shift(2*DOWN)
        dot3b = always_redraw(lambda:
            Dot().shift(3*DOWN+vt.get_value()*RIGHT)
        )
        def updater(mobj):
            mobj.set_x(vt.get_value())
        dot1a.add_updater(updater)
        dot1b.add_updater(updater)
        self.play(
            LaggedStart(
                Create(dot1a),
                Create(dot2a),
                Create(dot3a)
            )
        )
        self.play(
            Create(dot1b),
            Create(dot2b),
            Create(dot3b)
        )
        dot2a.add_updater(updater)
        dot2b.add_updater(updater)
        dot1a.resume_updating()
        dot2a.resume_updating()
        dot3a.resume_updating()
        self.wait()
        self.play(vt.animate.set_value(7),run_time=4)
        self.wait()

https://github.com/user-attachments/assets/866de534-dac7-4f17-9a77-575dd30e8ba3

Laifsyn commented 1 month ago

A possible fix is to write at composition.py, class: AnimationGroup(Animation) method: begin(self)

    def begin(self) -> None:
        if not self.animations:
            raise ValueError(
                f"Trying to play {self} without animations, this is not supported. "
                "Please add at least one subanimation."
            )

        self.anim_group_time = 0.0
        if self.suspend_mobject_updating:
            self.group.suspend_updating()
        for anim in self.animations:
            anim.begin()
+        else:
+            for anim in self.animations:
+                anim.finish()

I'm actually not sure what's the semantics for else: , but according to what I've read, it's meant to be used as a cleanup which made sense for me in the context of this fix

movimentum commented 1 week ago

Oh, I'd better look for this bug first than digging into why my old scenes stoped rendering... Solution was similar to @uwezi: mob.resume_updating() after LaggedStart.