Repository: ManimCommunity/manim
File: manim/animation/speedmodifier.py (Python)

Code before:
        curr_time = 0
        for node, n in list(self.speedinfo.items())[1:]:
            dur = node - prevnode
            self.conditions.append(
                lambda x, curr_time=curr_time, m=m, n=n, dur=dur: curr_time / total_time
                <= x
                <= (curr_time + self.f_inv_1(m, n) * dur) / total_time
            )
            self.functions.append(
                lambda x, dur=dur, m=m, n=n, prevnode=prevnode, curr_time=curr_time: self.speed_modifier(
                    (total_time * x - curr_time) / dur, m, n
                )
                * dur
                + prevnode
            )
            curr_time += self.f_inv_1(m, n) * dur
            prevnode = node
            m = n

        def func(x):
            newx = piecewise(
                self.rate_func(x),
                [condition(self.rate_func(x)) for condition in self.conditions],
                self.functions,
            )
            ChangeSpeed.dt = (newx - self.t) * self.anim.run_time
            self.t = newx
            return newx

        self.anim.set_rate_func(func)

        super().__init__(
            self.anim.mobject,
            rate_func=self.rate_func,
            run_time=total_time * self.anim.run_time,
            **kwargs,
        )

    def setup(self, anim):
        if type(anim) is Wait:
            return ChangedWait(
                run_time=anim.run_time,
                stop_condition=anim.stop_condition,
                frozen_frame=anim.is_static_wait,
            )
        return prepare_animation(anim)

    def get_total_time(self) -> float:
        prevnode = 0
        m = self.speedinfo[0]
        total_time = 0

Review comment (suggestion):
Just thinking out loud: if the rate function modifies the class variable, then playing several `ChangeSpeed` animations simultaneously (or with a `lag_ratio`) will cause problems. Perhaps one could change it to instance variables instead, and bind `self` to the rate function as the default argument of an additional keyword argument; `def func(t, animation=self): ...` to get around that. Not sure whether this works.

Code after:
        # not set, set them to 1 and the penultimate factor, respectively
        if 0 not in speedinfo:
            speedinfo[0] = 1
        if 1 not in speedinfo:
            speedinfo[1] = sorted(speedinfo.items())[-1][1]

        self.speedinfo = dict(sorted(speedinfo.items()))
        self.functions = []
        self.conditions = []

        # Get the time taken by amimation if `run_time` is assumed to be 1
        scaled_total_time = self.get_scaled_total_time()

        prevnode = 0
        init_speed = self.speedinfo[0]
        curr_time = 0
        for node, final_speed in list(self.speedinfo.items())[1:]:
            dur = node - prevnode

            def condition(
                t,
                curr_time=curr_time,
                init_speed=init_speed,
                final_speed=final_speed,
                dur=dur,
            ):
                lower_bound = curr_time / scaled_total_time
                upper_bound = (
                    curr_time + self.f_inv_1(init_speed, final_speed) * dur
                ) / scaled_total_time
                return lower_bound <= t <= upper_bound

            self.conditions.append(condition)

            def function(
                t,
                curr_time=curr_time,
                init_speed=init_speed,
                final_speed=final_speed,
                dur=dur,
                prevnode=prevnode,
            ):
                return (
                    self.speed_modifier(
                        (scaled_total_time * t - curr_time) / dur,
                        init_speed,
                        final_speed,
                    )
                    * dur
                    + prevnode
                )