Search code examples
pythonmanim

Rectangle appearing before replacement transform


So I am trying to re-create googles logo using manim. The part where I am struggling is, the blue slab appears before transformation. My understanding of ReplacementTranform is, that the target does not appear before transformation and as animation proceeds it start to appear. But in my case somehow, the blue slab appears before the transformation.

Is my understanding of replacement transform correct or am i using it wrong?

from manim import *


class Props:
    blue = "#4285F4"
    red = "#DB4437"
    yellow = "#F4B400"
    green = "#0F9D58"

    dot_radius = 0.5

    arc_inner_radius = 1
    arc_outer_radius = 1.5
    arc_angle = PI / 2

    blue_start_angle = -PI / 4
    red_start_angle = -7 * PI / 4
    yellow_start_angle = -5 * PI / 4
    green_start_angle = -3 * PI / 4


class GoogleLogo(Scene):
    def create_dot(self, color, radius, **kwargs):
        return Dot(color=color, radius=radius, **kwargs)

    def arrange_dots(self, blue, red, yellow, green):
        blue.move_to((-3, 0, 0))
        red.move_to((-1, 0, 0))
        yellow.move_to((1, 0, 0))
        green.move_to((3, 0, 0))

    def annulus_sector(
        self,
        color,
        start_angle,
        angle=PI / 2,
        inner_radius=Props.arc_inner_radius,
        outer_radius=Props.arc_outer_radius,
    ):
        return AnnularSector(
            inner_radius=inner_radius,
            outer_radius=outer_radius,
            color=color,
            fill_opacity=1.0,
            start_angle=start_angle,
            angle=angle,
            stroke_width=0,
        )

    def construct(self):
        blue, red, green, yellow = [
            self.create_dot(c, Props.dot_radius)
            for c in (Props.blue, Props.red, Props.green, Props.yellow)
        ]

        self.arrange_dots(blue, red, yellow, green)

        dots = VGroup(blue, red, yellow, green)

        bounce_animation = [
            AnimationGroup(
                x.animate(rate_func=there_and_back).shift(UP * 0.5),
                x.animate(rate_func=there_and_back).shift(DOWN * 0.5),
            )
            for x in dots
        ]

        for _ in range(1):
            self.play(LaggedStart(*bounce_animation, lag_ratio=0.1))

        # transform dots to letter G
        blue_annulus, red_annulus, yellow_annulus, green_annulus = [
            self.annulus_sector(c, a, ang)
            for c, a, ang in zip(
                [Props.blue, Props.red, Props.yellow, Props.green],
                [
                    Props.blue_start_angle,
                    Props.red_start_angle,
                    Props.yellow_start_angle,
                    Props.green_start_angle,
                ],
                [PI / 4] + [PI / 2] * 3,
            )
        ]
        dots_minus_blue = VGroup(red, yellow, green)
        sectors_minus_blue = VGroup(red_annulus, yellow_annulus, green_annulus)

        transformations = [
            ReplacementTransform(dot, sect, path_func=utils.paths.path_along_arc(-PI))
            for dot, sect in zip(dots_minus_blue, sectors_minus_blue)
        ]

        blue_slab = Rectangle(height=0.5, width=1.5, color=Props.blue, fill_opacity=1)
        blue_slab.next_to(blue_annulus, UP, aligned_edge=UR, buff=0)
        blue_slab.set_stroke(width=0)
        blue_slab.shift(UP * 0.1)
        blue_annulus.set_stroke(width=0)

        dot_to_slab = ReplacementTransform(blue, blue_slab)
        slab_to_annulus = ReplacementTransform(blue_slab.copy(), blue_annulus)

        self.play(
            LaggedStart(
                *transformations,
                Succession(dot_to_slab, slab_to_annulus),
                lag_ratio=0.9
            )
        )

        self.wait(3)

Solution

  • Your understanding of ReplacementTransform is indeed wrong. Using ReplacementTransform just means that the object is replaced in memory. I think this YouTube tutorial explains it quite well even if it's set as outdated.

    The problem here seems to be that manim always shows objects which are replaced in a transformation at the start of the play()-function call. A simple solution would be to set the last transformation in an extra function call:

            dot_to_slab = ReplacementTransform(blue, blue_slab)
            slab_to_annulus = ReplacementTransform(blue_slab.copy(), blue_annulus)
    
            self.play(
                LaggedStart(
                    *transformations,
                    Succession(dot_to_slab),
                    lag_ratio=0.9
                )
            )
            self.play(slab_to_annulus)