跳转至

移动与变形

影片代码
import manimgx as m
import numpy as np


class MoveAndDeformHero(m.Scene):
    def construct(self) -> None:
        grid = m.NumberPlane(
            x_range=[-4, 4, 1], y_range=[-3, 3, 1]
        ).prepare_for_nonlinear_transform()
        self.add(grid)
        self.play(m.ApplyMatrix([[1, 1], [0, 1]], grid), run_time=2)
        self.play(
            m.ApplyFunction(
                lambda mob: mob.apply_function(
                    lambda p: p + 0.3 * np.array([np.sin(p[1]), np.sin(p[0]), 0])
                ),
                grid,
            ),
            run_time=2,
        )
        self.wait()

某些变化不是方法调用:绕某点旋转且 mobject 持续转动、沿路径行进、每个点的变形。这些 动画实现它们。

旋转与跟随

Rotate

代码
import manimgx as m


class RotateExample(m.Scene):
    def construct(self) -> None:
        pivot = m.Dot(radius=0.1)
        left = m.Rectangle(m.BLUE, width=2.5, height=1.2, fill_opacity=0.5)
        right = left.copy().set_color(m.YELLOW)
        left.shift(4 * m.LEFT)
        right.shift(2 * m.RIGHT)
        self.add(pivot, left, right)
        self.play(
            m.Rotate(left, m.PI / 2),
            m.Rotate(right, m.PI / 2, about_point=pivot.get_center()),
            run_time=2,
        )

将一个 mobject 绕某点刚性旋转。

它绕 axis 轴转过 angle 角度,围绕一个枢轴:about_point,或其边界框上 about_edge 方向的点,或其中心,该枢轴取自旋转开始时 mobject 的状态。 每个点沿绕枢轴的圆周移动,因此 mobject 在途中保持其形状, 而 TAU 的角度为一整圈。

m.Rotate(mobject, angle=PI, axis=OUT, about_point=None, about_edge=None, **kwargs)
mobject

要旋转的 mobject。

angle

旋转角度,以弧度为单位,绕 axis 逆时针方向。

axis

旋转所绕的轴;默认为 OUT,即在屏幕平面内旋转。

about_point

枢轴;None 表示取自 about_edge 或中心。

about_edge

未给定 about_point 时,所绕的边界框上点的方向。

run_time

动画播放时长,以秒为单位(默认 1)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 变换关键字。

源码

manimgx\animation\motion.py

def __init__(
    self,
    mobject: Mobject,
    angle: float = PI,
    axis: Vector3DLike = OUT,
    about_point: Point3DLike | None = None,
    about_edge: Vector3DLike | None = None,
    **kwargs: Unpack[TransformOptions],
) -> None:
    self.angle, self.axis, self.about_edge = angle, axis, about_edge
    self._point = None if about_point is None else np.asarray(about_point, float)
    self.about_point = self._pivot(mobject)
    self._own_path = "path_func" in kwargs or "path_arc_centers" in kwargs
    kwargs.setdefault("path_arc", angle)
    kwargs.setdefault("path_arc_axis", axis)
    kwargs.setdefault("path_arc_centers", self.about_point)
    super().__init__(
        mobject,
        keys=(None, self._turned),
        **kwargs,
    )

Rotating

代码
import manimgx as m


class RotatingExample(m.Scene):
    def construct(self) -> None:
        hand = m.Arrow(m.ORIGIN, 2.5 * m.UP, buff=0, color=m.YELLOW)
        self.add(m.Circle(radius=3, color=m.GREY), hand)
        self.play(m.Rotating(hand, -m.TAU, about_point=m.ORIGIN, run_time=4))

以匀速旋转一个 mobject:默认为 5 秒内转一整圈。

即一个角度为 TAU(除非另行指定)的 Rotate, 以匀速(linear)进行,历时 5 秒。

m.Rotating(mobject, angle=TAU, axis=OUT, about_point=None, about_edge=None, **kwargs)
mobject

要旋转的 mobject。

angle

旋转角度,以弧度为单位,绕 axis 逆时针方向。

axis

旋转所绕的轴;默认为 OUT,即在屏幕平面内旋转。

about_point

枢轴;None 表示取自 about_edge 或中心。

about_edge

未给定 about_point 时,所绕的边界框上点的方向。

run_time

动画播放时长,以秒为单位(默认 1)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 变换关键字。

源码

manimgx\animation\motion.py

def __init__(
    self,
    mobject: Mobject,
    angle: float = TAU,
    axis: Vector3DLike = OUT,
    about_point: Point3DLike | None = None,
    about_edge: Vector3DLike | None = None,
    **kwargs: Unpack[TransformOptions],
) -> None:
    super().__init__(mobject, angle, axis, about_point, about_edge, **kwargs)

MoveAlongPath

代码
import numpy as np

import manimgx as m


class MoveAlongPathExample(m.Scene):
    def construct(self) -> None:
        path = m.FunctionGraph(lambda x: 2 * np.sin(x), x_range=(-6, 6))
        dot = m.Dot(radius=0.2, color=m.ORANGE)
        self.add(path.set_color(m.GREY), dot)
        self.play(m.MoveAlongPath(dot, path, run_time=3))

沿一条路径移动一个 mobject。

其中心沿 path 从路径起点移动到终点,沿其长度以匀速行进 (由速率函数缓动),该 mobject 保持其形状。它从路径起点开始, 无论其原先位于何处。其自身的更新器在移动期间直接作用于它 (suspend_mobject_updating 为 False)。

m.MoveAlongPath(mobject, path, **kwargs)
mobject

要移动的 mobject。

path

它所沿行的曲线。

run_time

动画播放时长,以秒为单位(默认 1)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 变换关键字。

源码

manimgx\animation\motion.py

def __init__(
    self, mobject: Mobject, path: VMobject, **kwargs: Unpack[TransformOptions]
) -> None:
    self.path = path
    kwargs["path_func"] = Path(steps=(path.point_from_proportion,))
    super().__init__(
        mobject,
        keys=tuple(
            (
                lambda m, end=end: m.move_to(path.point_from_proportion(end))
                for end in (0, 1)
            )
        ),
        **kwargs,
    )

经过函数

mobject 的每个点前往函数将其映射到的位置。弯曲的映像需要更多点:先插入曲线 (参见 insert_n_curves)。

ApplyFunction

代码
import manimgx as m


class ApplyFunctionExample(m.Scene):
    def construct(self) -> None:
        square = m.Square(side_length=3, color=m.BLUE, fill_opacity=0.5)
        self.add(square)

        def to_red_circle(mob: m.Mobject) -> m.Mobject:
            circle = m.Circle(radius=2, color=m.RED, fill_opacity=0.5)
            return circle.move_to(mob)

        self.play(m.ApplyFunction(to_red_circle, square))

将一个 mobject 变换为一个函数对其作用后的结果。

该函数在动画开始时收到该 mobject 的副本,并返回要变换到的状态: 即该副本经修改后的结果,或一个新的 mobject(其他返回值会引发 TypeError)。

m.ApplyFunction(function, mobject, **kwargs)
function

从该 mobject 的副本到其终止状态的函数。

mobject

要变换的 mobject。

run_time

动画播放时长,以秒为单位(默认 1)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 变换关键字。

源码

manimgx\animation\motion.py

def __init__(
    self,
    function: Callable[[Mobject], Mobject],
    mobject: Mobject,
    **kwargs: Unpack[TransformOptions],
) -> None:
    self.function = function
    super().__init__(mobject, keys=(None, self._apply), **kwargs)

ApplyPointwiseFunction

代码
import numpy as np

import manimgx as m


class ApplyPointwiseFunctionExample(m.Scene):
    def construct(self) -> None:
        square = m.Square(side_length=2, color=m.BLUE, fill_opacity=0.5)
        self.add(square)
        self.play(
            m.ApplyPointwiseFunction(
                lambda p: m.complex_to_R3(np.exp(m.R3_to_complex(p))), square
            )
        )

将一个 mobject 变换为在一个点的函数下的像。

每个点 p 沿直线移动到 function(p),路径的曲线按 apply_function 的方式映射, 在函数使其弯曲处被切分为更多曲线。运行 3 秒。

m.ApplyPointwiseFunction(function, mobject, **kwargs)
function

从一个点到其去向的函数,以场景坐标表示。

mobject

要移动的 mobject。

run_time

动画播放时长,以秒为单位(默认 1)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 变换关键字。

源码

manimgx\animation\motion.py

def __init__(
    self,
    function: Callable[[Point3D], Point3D],
    mobject: Mobject,
    **kwargs: Unpack[TransformOptions],
) -> None:
    super().__init__(mobject.apply_function, function, **kwargs)

ApplyMatrix

代码
import manimgx as m


class ApplyMatrixExample(m.Scene):
    def construct(self) -> None:
        matrix = [[1, 1], [0, 2 / 3]]
        plane = m.NumberPlane()
        text = m.Text("Hello World!", font_size=72)
        self.add(plane, text)
        self.play(m.ApplyMatrix(matrix, plane), m.ApplyMatrix(matrix, text))

以矩阵变换一个 mobject:每个点 p 变为 M·p。

2 × 2 矩阵作用于 x 和 y,保持 z 不变;3 × 3 矩阵作用于全部三个坐标 (其他形状会引发 ValueError)。各点沿直线移动。运行 3 秒。

m.ApplyMatrix(matrix, mobject, about_point=ORIGIN, **kwargs)
matrix

矩阵,2 × 2 或 3 × 3,以嵌套列表或数组表示。

mobject

要变换的 mobject。

about_point

保持不动的点。

run_time

动画播放时长,以秒为单位(默认 1)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 变换关键字。

源码

manimgx\animation\motion.py

def __init__(
    self,
    matrix: npt.ArrayLike,
    mobject: Mobject,
    about_point: Point3DLike = ORIGIN,
    **kwargs: Unpack[TransformOptions],
) -> None:
    matrix = np.array(matrix)
    if matrix.shape == (2, 2):
        matrix = np.block(
            [[matrix, np.zeros((2, 1))], [np.zeros((1, 2)), np.ones((1, 1))]]
        )
    elif matrix.shape != (3, 3):
        raise ValueError("Matrix has bad dimensions")
    center = np.asarray(about_point, dtype=float)
    super().__init__(
        lambda p: np.dot(p - center, matrix.T) + center, mobject, **kwargs
    )

ApplyComplexFunction

代码
import manimgx as m


class ApplyComplexFunctionExample(m.Scene):
    def construct(self) -> None:
        plane = m.NumberPlane(x_range=(-3, 3, 0.5), y_range=(-3, 3, 0.5))
        plane.prepare_for_nonlinear_transform()
        self.add(plane)
        self.play(m.ApplyComplexFunction(lambda z: z**2 / 4, plane), run_time=2)

将一个 mobject 变换为在一个复数函数下的像。

每个点 (x, y, z) 被视为 x + iy,沿直线移动到其在该函数下的像, 保持 z 不变,路径的曲线按 apply_function 的方式映射。

m.ApplyComplexFunction(function, mobject, **kwargs)
function

从复数到复数的函数。

mobject

The mobject to move.

run_time

动画播放时长,以秒为单位(默认 1)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 变换关键字。

源码

manimgx\animation\motion.py

def __init__(
    self,
    function: Callable[[complex], complex],
    mobject: Mobject,
    **kwargs: Unpack[TransformOptions],
) -> None:
    self.function = function
    super().__init__(mobject.apply_complex_function, function, **kwargs)

随时间变形

Homotopy

代码
import numpy as np

import manimgx as m


class HomotopyExample(m.Scene):
    def construct(self) -> None:
        line = m.FunctionGraph(lambda x: 0, x_range=(-6, 6), color=m.BLUE)

        def wave(x: float, y: float, z: float, t: float) -> tuple[float, ...]:
            return (x, y + 1.5 * t * np.sin(2 * x - 6 * t), z)

        self.add(line)
        self.play(m.Homotopy(wave, line))

以同伦变形一个 mobject:一个关于每个点与进度的函数。

在进度 t(从 0 到 1,由速率函数缓动)处,该 mobject 为其初始状态的像: 每个点 (x, y, z) 变为 homotopy(x, y, z, t),路径的曲线按 apply_function 的方式映射。运行 3 秒。

m.Homotopy(homotopy, mobject, apply_function_kwargs=None, **kwargs)
homotopy

关于 x、y、z 和 t 的函数,返回该点在 t 处的位置。

mobject

要变形的 mobject。

apply_function_kwargs

坐标度量的基准位置: {"about_point": ...} 或 {"about_edge": ...};None 表示以原点为基准。

run_time

动画播放时长,以秒为单位(默认 1)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 动画关键字。

源码

manimgx\animation\motion.py

def __init__(
    self,
    homotopy: Callable[[float, float, float, float], Point3DLike],
    mobject: Mobject,
    apply_function_kwargs: Pivot | None = None,
    **kwargs: Unpack[AnimationOptions],
) -> None:
    self.homotopy = homotopy
    self.apply_function_kwargs: Pivot = apply_function_kwargs or {}
    super().__init__(mobject, **kwargs)

ComplexHomotopy

代码
import numpy as np

import manimgx as m


class ComplexHomotopyExample(m.Scene):
    def construct(self) -> None:
        plane = m.NumberPlane(x_range=(-3, 3), y_range=(-3, 3))
        plane.prepare_for_nonlinear_transform()
        self.add(plane)
        self.play(
            m.ComplexHomotopy(lambda z, t: z * np.exp(0.5j * t * abs(z)), plane)
        )

以复同伦变形一个 mobject:一个关于 x + iy 与进度的函数。

在进度 t(从 0 到 1,由速率函数缓动)处,该 mobject 为其初始状态的像: 每个点 (x, y, z) 变为 complex_homotopy(x + iy, t),保持 z 不变, 路径的曲线按 apply_function 的方式映射。 运行 3 秒。

m.ComplexHomotopy(complex_homotopy, mobject, **kwargs)
complex_homotopy

关于 z 和 t 的函数,返回该点在 t 处的位置,以复数表示。

mobject

要变形的 mobject。

run_time

动画播放时长,以秒为单位(默认 1)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 动画关键字。

源码

manimgx\animation\motion.py

def __init__(
    self,
    complex_homotopy: Callable[[complex, float], complex],
    mobject: Mobject,
    **kwargs: Unpack[AnimationOptions],
) -> None:
    def homotopy(
        x: float, y: float, z: float, t: float
    ) -> tuple[float, float, float]:
        c = complex_homotopy(complex(x, y), t)
        return (c.real, c.imag, z)

    super().__init__(homotopy, mobject, **kwargs)

ApplyWave

代码
import manimgx as m


class ApplyWaveExample(m.Scene):
    def construct(self) -> None:
        text = m.Text("WaveWaveWave", font_size=120)
        self.add(text)
        self.play(m.ApplyWave(text))
        self.play(m.ApplyWave(text, ripples=3, amplitude=0.4))

让一个波从左到右穿过一个 mobject。

当波经过时,其各点沿 direction 方向位移,至多为 amplitude, 波过后各点回到原位。运行 2 秒。

m.ApplyWave(mobject, direction=UP, amplitude=0.2, wave_func=smooth, time_width=1, ripples=1, **kwargs)
mobject

要起波的 mobject。

direction

各点位移的方向。

amplitude

各点至多位移的距离,以场景单位表示。

wave_func

每个波纹升起的形状:一个速率函数。

time_width

波的宽度,以该 mobject 宽度的分数表示。

ripples

波所含的波纹数。

run_time

动画播放时长,以秒为单位(默认 1)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 动画关键字。

源码

manimgx\animation\motion.py

def __init__(
    self,
    mobject: Mobject,
    direction: Vector3DLike = UP,
    amplitude: float = 0.2,
    wave_func: Callable[[float], float] = smooth,
    time_width: float = 1,
    ripples: int = 1,
    **kwargs: Unpack[AnimationOptions],
) -> None:
    x_min, x_max = mobject.get_left()[0], mobject.get_right()[0]
    vect = amplitude * normalize(direction)

    def wave(t: float) -> float:
        t = 1 - t
        if t >= 1 or t <= 0:
            return 0
        phases = ripples * 2
        phase = int(t * phases)
        if phase == 0:
            return wave_func(t * phases)
        if phase == phases - 1:
            t -= phase / phases
            return (1 - wave_func(t * phases)) * (2 * (ripples % 2) - 1)
        phase = int((phase - 1) / 2)
        t -= (2 * phase + 1) / phases
        return (1 - 2 * wave_func(t * ripples)) * (1 - 2 * (phase % 2))

    def homotopy(x: float, y: float, z: float, t: float) -> np.ndarray:
        upper = interpolate(0, 1 + time_width, t)
        relative_x = inverse_interpolate(x_min, x_max, x)
        return (
            np.array([x, y, z])
            + wave(inverse_interpolate(upper - time_width, upper, relative_x))
            * vect
        )

    super().__init__(homotopy, mobject, **kwargs)

PhaseFlow

代码
import numpy as np

import manimgx as m


class PhaseFlowExample(m.Scene):
    def construct(self) -> None:
        square = m.Square(side_length=2, color=m.BLUE, fill_opacity=0.5)
        square.shift(3 * m.RIGHT)
        self.add(m.Dot(), square)

        def spiral(p: np.ndarray) -> np.ndarray:
            return np.array([-p[1] - 0.3 * p[0], p[0] - 0.3 * p[1], 0.0])

        self.play(m.PhaseFlow(spiral, square, virtual_time=m.PI, run_time=3))

将一个 mobject 的各点沿一个向量场携带。

每个点以 function 在其所在位置给出的速度移动,在动画期间流过 virtual_time 个场的时间单位,从动画开始时其所在位置出发。 该流以场的时间为步长积分,每秒动画 config.simulation_rate 步, 采用经典 Runge–Kutta 方法,因此在任何帧率下结果相同; 步与步之间的帧显示从前一步继续的流。路径的曲线按 apply_function 的方式映射。 动画以匀速进行(linear),且该 mobject 自身的更新器直接作用于它 (suspend_mobject_updating 为 False)。

m.PhaseFlow(function, mobject, virtual_time=1, **kwargs)
function

向量场:从一个点到其速度的函数。

mobject

要携带的 mobject。

virtual_time

各点流动的时长,以场的时间单位表示。

run_time

动画播放时长,以秒为单位(默认 1)。

lag_ratio

mobject 各部分如何交错:每个部分在其前一个部分开始后,经过其运行时长的该分数比例后开始 (默认 0:全部同时;1:一个接一个)。

rate_func

动画进度如何随时间运行:一个从 [0, 1] 到 [0, 1] 的函数 (默认为 smooth;参见 rate functions)。

reverse_rate_function

是否反向运行动画(默认 False)。

name

动画的名称。

remover

动画结束时 mobject 是否离开场景 (默认 False)。

suspend_mobject_updating

动画期间 mobject 的更新器是否在其下运行(默认 True):它们 持续作用于 mobject,每一帧显示动画应用于其结果。 若为 False,则它们作用于被动画的 mobject 本身。

introducer

动画开始时 mobject 是否加入场景(默认 False); 否则若场景缺少它,play 在其开始时将其带入。

use_override

若某 mobject 的类用另一个动画代替此动画播放,是否如此执行 (默认 True)。

还接受 动画关键字。

源码

manimgx\animation\motion.py

def __init__(
    self,
    function: Callable[[Point3D], Point3D],
    mobject: Mobject,
    virtual_time: float = 1,
    **kwargs: Unpack[AnimationOptions],
) -> None:
    self.virtual_time, self.function = virtual_time, function
    super().__init__(mobject, **kwargs)