ManimCommunity / manim

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

Unexpected Behavior with Create then Uncreate in Succession #3133

Open helblazer811 opened 1 year ago

helblazer811 commented 1 year ago

Description of bug / unexpected behavior

I am getting unexpected behavior when I try to Create and then Uncreate something (specifically a Dot in the example below). The red Dot is present at the beginning of the animation when it would only appear after it is created. This issue only happens when I try to subsequently Uncreate the Dot. Is there a different way of doing this? Am I misunderstanding the functionality of Succession, or is this a bug? Thanks so much!

Expected behavior

I expect the red Dot to only appear after the white Dot is created and shifted.

How to reproduce the issue

This is the code that reproduces the issue.

from manim import *

class TestSuccession(Scene):

    def construct(self):
        white_dot = Dot(color=WHITE)
        white_dot.shift(UP)

        red_dot = Dot(color=RED)

        self.play(
            Succession(
                Create(white_dot),
                white_dot.animate.shift(RIGHT),
                Create(red_dot),
                Wait(1),
                Uncreate(red_dot),
            )
        )

Additional media files

https://user-images.githubusercontent.com/14181830/214108912-c83b8d77-4e21-4496-ab59-d32c2324d22b.mp4

System specifications

chunribu commented 1 year ago

Hi @helblazer811 . You can get there temporarily this way

from manim import *

class Uncreate(Create):
    def __init__(
        self,
        mobject,
        reverse_rate_function: bool = True,
        introducer: bool = False,
        remover: bool = True,
        **kwargs,
    ) -> None:
        super().__init__(
            mobject,
            reverse_rate_function=reverse_rate_function,
            introducer=introducer,
            remover=remover,
            **kwargs,
        )

class TestSuccession(Scene):
    def construct(self):
        white_dot = Dot(color=WHITE)
        white_dot.shift(UP)

        red_dot = Dot(color=RED)

        self.play(
            Succession(
                Create(white_dot),
                white_dot.animate.shift(RIGHT),
                Create(red_dot),
                Wait(1),
                Uncreate(red_dot, introducer=True),
            )
        )

This hacked class Uncreate adds a parameter introducer, set it True and the mobject will not appear at the very beginning.

https://user-images.githubusercontent.com/57521167/214758561-d127e092-ac54-4c2d-96f1-50d194996a32.mp4

helblazer811 commented 1 year ago

Thanks so much for the help with the workaround! This is very helpful.

vaclavblazej commented 1 year ago

Bug is here. It should check that first object's anim is an introducer and ignore any later appearances.

    if not anim.is_introducer() and anim.mobject is not None:
        scene.add(anim.mobject)
vaclavblazej commented 1 year ago

Nevermind, ignore my previous comment. Succession creates a single scene, hence, it sets up the scene first, and then runs animations in succession (possibly overlapping). I think it is the bug lies in that _setup_scene adds the object in a visible state when animation is an introducer because if start of the animation is postponed (as in this case), the object is visible from start until the animation is played.

tobiasBora commented 1 year ago

might be related to another I have (see here https://github.com/ManimCommunity/manim/issues/3338):

from manim import *
config.background_color = WHITE

class Intro(ThreeDScene):
    def construct(self):
        Mobject.set_default(color=BLACK)
        libA = VGroup(Rectangle(color=BLUE), MathTex(r"\sqrt{\Theta}"))
        libB = VGroup(Rectangle(color=RED), MathTex(r"\sqrt{\Delta}"))
        self.play(FadeIn(libA))
        libB.rotate(-PI/2, axis=(0,1,0))
        self.play(Succession(
            AnimationGroup(Rotate(libA, angle=PI/2, axis=(0,1,0),rate_func=rate_functions.linear),run_time=.5),
            AnimationGroup(
                FadeIn(libB, run_time=1/25),
                FadeOut(libA, run_time=1/25)),
            # This also fails:
            # AnimationGroup(
            #     Transform(libA, libB, run_time=1/25)),
            AnimationGroup(Rotate(libB, angle=PI/2, axis=(0,1,0),rate_func=rate_functions.linear), run_time=.5),
            rate_func=rate_functions.ease_in_out_cubic
        ))

        self.wait()

You can still see a weird thing, with a red line appearing in between (seems like the last item that is added before the actual animation starts):

https://github.com/ManimCommunity/manim/assets/2164118/4e3f52c1-97db-4291-80af-4f5914669f9d

if you try with

class Intro(ThreeDScene):
    def construct(self):
        Mobject.set_default(color=BLACK)
        libA = VGroup(Rectangle(color=BLUE), MathTex(r"\sqrt{\Theta}"))
        libB = VGroup(Rectangle(color=RED), MathTex(r"\sqrt{\Delta}"))
        self.play(FadeIn(libA))
        # Make libB replace libA while rotating the elements
        libB.rotate(-PI/2, axis=(0,1,0))
        self.play(Succession(
            AnimationGroup(Rotate(libA, angle=PI/2, axis=(0,1,0),rate_func=rate_functions.linear),run_time=.5),
            # This fails (libB is visible from the start):
            # AnimationGroup(
            #     FadeIn(libB, run_time=1/25),
            #     FadeOut(libA, run_time=1/25)),
            # # This also fails:
            AnimationGroup(
                Transform(libA, libB, run_time=1/25)),
            AnimationGroup(Rotate(libB, angle=PI/2, axis=(0,1,0),rate_func=rate_functions.linear), run_time=.5),
            rate_func=rate_functions.ease_in_out_cubic
        ))

        self.wait()

the result is even more weird as the line stays forever.

Note that the bug is also present with AnimationGroup(…, lag_ratio=1)