mscene.animΒΆ

from manim import *


class DashFlow(DashedVMobject):
    """
    A dashed VMobject that continuously updates its offset for a dash flowing effect.

    Args:
        vmob (VMobject): The VMobject to be dashed and animated.
        rate (float, optional): The rate of shifting dash offset over time. Defaults to 1.0.
        **kwargs: Additional keyword arguments passed to DashedVMobject.

    Methods:
        pause():
            Suspend the dash animation.
        resume():
            Resume the dash animation.
        clear():
            Remove all updaters.
    """

    def __init__(self, vmob, rate=1, **kwargs):
        super().__init__(vmob, **kwargs)
        self.offset = 0

        def _updater(m, dt):
            self.offset = (rate * dt + self.offset) % 1
            m.become(DashedVMobject(vmob, dash_offset=self.offset, **kwargs))

        self.add_updater(_updater)

    def pause(self):
        self.suspend_updating()

    def resume(self):
        self.resume_updating()

    def clear(self):
        self.clear_updaters()


class DrawArc(Succession):
    def __init__(self, arc, reverse=False, run_time=2, **kwargs):
        quarter_time = run_time / 4
        half_time = run_time / 2

        if reverse:
            line = Line(
                arc.get_arc_center(),
                arc.get_end(),
                stroke_width=arc.get_stroke_width(),
                stroke_color=arc.get_stroke_color(),
            )
            create_arc = Uncreate(arc, run_time=half_time)
            rotate_line = Rotate(
                line, -arc.angle, about_point=arc.get_arc_center(), run_time=half_time
            )
        else:
            line = Line(
                arc.get_arc_center(),
                arc.get_start(),
                stroke_width=arc.get_stroke_width(),
                stroke_color=arc.get_stroke_color(),
            )
            create_arc = Create(arc, run_time=half_time)
            rotate_line = Rotate(
                line, arc.angle, about_point=arc.get_arc_center(), run_time=half_time
            )

        super().__init__(
            Create(line, run_time=quarter_time),
            AnimationGroup(
                create_arc,
                rotate_line,
            ),
            Uncreate(line, run_time=quarter_time),
            **kwargs,
        )


class FlashFade(AnimationGroup):
    """
    Animation for VMobject to fade in or out with flashing outline effect.

    Args:
        vmob: The VMobject to animate.
        mode: IN for fade-in, OUT for fade-out, None for fade-in then fade-out. Defaults to None.
        reverse: If True, reverses the animation direction. Defaults to False.
        color: The color of the flash outline. Defaults to None.
        width: The stroke width of the flash outline. Defaults to None.
        time_width: The relative duration of the flash effect. Defaults to 0.5.
        run_time: The duration of each sub-animation. Defaults to 1.0.
        lag_ratio: The lag ratio between sub-animations. Defaults to 0.125.
        **kwargs: Additional keyword arguments for AnimationGroup.
    """

    def __init__(
        self,
        vmob: VMobject,
        mode=None,
        reverse=False,
        color=None,
        width=None,
        time_width=0.5,
        run_time=1.0,
        lag_ratio=0.125,
        **kwargs,
    ):
        if reverse:
            vmob = vmob[::-1]
            vcopy = vmob.copy()
            vcopy.set_fill(opacity=0).set_stroke(color=color, width=width)
            for vm in vcopy:
                vm = vm.reverse_direction()
        else:
            vcopy = vmob.copy()
            vcopy.set_fill(opacity=0).set_stroke(color=color, width=width)

        n = len(vmob)
        anim = []

        if mode is IN:
            for i in range(n):
                anim.append(
                    AnimationGroup(
                        FadeIn(vmob[i]),
                        ShowPassingFlash(vcopy[i], time_width=time_width),
                        run_time=run_time,
                    )
                )
        elif mode is OUT:
            for i in range(n):
                anim.append(
                    AnimationGroup(
                        FadeOut(vmob[i]),
                        ShowPassingFlash(vcopy[i], time_width=time_width),
                        run_time=run_time,
                    )
                )
        else:
            for i in range(n):
                anim.append(
                    AnimationGroup(
                        FadeIn(vmob[i].copy(), rate_func=there_and_back_with_pause),
                        ShowPassingFlash(vcopy[i], time_width=time_width),
                        run_time=run_time,
                    )
                )

        super().__init__(*anim, lag_ratio=lag_ratio, **kwargs)