跳转至

副本与状态

影片代码
import manimgx as m


class CopiesAndStatesHero(m.Scene):
    def construct(self) -> None:
        square = m.Square(color=m.BLUE, fill_opacity=0.6)
        self.play(m.Create(square))
        square.save_state()
        copy = square.copy().set_color(m.YELLOW)
        self.play(copy.animate.shift(3 * m.RIGHT))
        target = square.generate_target()
        target.shift(3 * m.LEFT).rotate(m.PI / 4).set_color(m.RED)
        self.play(m.MoveToTarget(square))
        self.play(m.Restore(square))
        self.wait()

一个 mobject 是场景中的一个对象:命名它的变量即命名该对象,对它的更改会显示在它 所在的任何位置。要拥有两个,就复制它。要更改一个 mobject 再回到原样,先保存其 状态。要将一个更改动画化为一个你逐步准备的状态,就生成它的目标。

copy

mobject 的拷贝,连同其整个家族。

其属性所引用的每个 mobject 都被拷贝一次,因此引用其某个成员的属性会引用拷贝的 对应成员。拷贝运行相同的更新器函数,但作用于自身,且不在场景中。拷贝代价低廉: 拷贝共享原件的点和样式,直到其中一方修改它们。

mobject.copy()
源码

manimgx\mobject.py

def copy(self) -> Self:
    """mobject 的拷贝,连同其整个家族。

    其属性所引用的每个 mobject 都被拷贝一次,因此引用其某个成员的属性会引用拷贝的
    对应成员。拷贝运行相同的更新器函数,但作用于自身,且不在场景中。拷贝代价低廉:
    拷贝共享原件的点和样式,直到其中一方修改它们。
    """
    return copy.deepcopy(self)

保存状态

save_state

代码
import manimgx as m


class MobjectSaveStateExample(m.Scene):
    def construct(self) -> None:
        square = m.Square(color=m.BLUE, fill_opacity=0.5)
        square.save_state()
        self.add(square)
        self.play(square.animate.set_color(m.YELLOW).shift(3 * m.LEFT))
        self.play(square.animate.rotate(m.PI / 4).scale(2))
        self.play(m.Restore(square))

保存 mobject 当前状态的副本,以供 restore 或 Restore 动画恢复。

该副本即为 mobject 的 saved_state;再次保存会替换它。

mobject.save_state()
源码

manimgx\mobject.py

def save_state(self) -> Self:
    """保存 mobject 当前状态的副本,以供
    [restore][manimgx.Mobject.restore] 或 [Restore][manimgx.Restore] 动画恢复。

    该副本即为 mobject 的 `saved_state`;再次保存会替换它。

    Examples:
        ```python
        import manimgx as m


        class MobjectSaveStateExample(m.Scene):
            def construct(self) -> None:
                square = m.Square(color=m.BLUE, fill_opacity=0.5)
                square.save_state()
                self.add(square)
                self.play(square.animate.set_color(m.YELLOW).shift(3 * m.LEFT))
                self.play(square.animate.rotate(m.PI / 4).scale(2))
                self.play(m.Restore(square))
        ```
    """
    self.saved_state = None  # not copied into the new saved state
    self.saved_state = self.copy()
    return self

restore

使该 mobject 恢复为 save_state 上次保存它时的状态: 其点位与样式逐成员地恢复(参见 become)。

若其状态从未被保存过,则抛出异常。

mobject.restore()
源码

manimgx\mobject.py

def restore(self) -> Self:
    """使该 mobject 恢复为 [save_state][manimgx.Mobject.save_state] 上次保存它时的状态:
    其点位与样式逐成员地恢复(参见 [become][manimgx.Mobject.become])。

    若其状态从未被保存过,则抛出异常。
    """
    saved = getattr(self, "saved_state", None)
    if saved is None:
        raise Exception("Trying to restore without having saved")
    self.become(saved)
    return self

准备状态

generate_target

代码
import manimgx as m


class MobjectGenerateTargetExample(m.Scene):
    def construct(self) -> None:
        circle = m.Circle(radius=1.5, color=m.BLUE).shift(3 * m.LEFT)
        target = circle.generate_target()
        target.set_fill(m.GREEN, opacity=0.5).shift(6 * m.RIGHT + m.UP)
        target.scale(0.5)
        self.add(circle)
        self.play(m.MoveToTarget(circle))

创建 mobject 的 target:它的一个拷贝,你将其 改变为某种状态,然后 MoveToTarget 将 mobject 动画移动到该状态。

使用 animate 也会创建一个新的目标, 替代此目标。

mobject.generate_target()

返回 该目标。

源码

manimgx\mobject.py

def generate_target(self) -> Self:
    """创建 mobject 的 [target][manimgx.Mobject.target]:它的一个拷贝,你将其
    改变为某种状态,然后 [MoveToTarget][manimgx.MoveToTarget] 将 mobject 动画移动到该状态。

    使用 [animate][manimgx.Mobject.animate] 也会创建一个新的目标,
    替代此目标。

    Returns:
        该目标。

    Examples:
        ```python
        import manimgx as m


        class MobjectGenerateTargetExample(m.Scene):
            def construct(self) -> None:
                circle = m.Circle(radius=1.5, color=m.BLUE).shift(3 * m.LEFT)
                target = circle.generate_target()
                target.set_fill(m.GREEN, opacity=0.5).shift(6 * m.RIGHT + m.UP)
                target.scale(0.5)
                self.add(circle)
                self.play(m.MoveToTarget(circle))
        ```
    """
    self.target = None  # not copied into the new target
    self.target = self.copy()
    return self.target

target

MoveToTarget 将 mobject 移动到的状态:由 generate_target 创建,或在创建 mobject 时给定; 若两者皆无则为 None。

变为另一个的样子

become

代码
import manimgx as m


class MobjectBecomeExample(m.Scene):
    def construct(self) -> None:
        shape = m.Circle(radius=2, color=m.RED, fill_opacity=0.8)
        shape.add_updater(lambda mob, dt: mob.rotate(dt))
        self.add(shape)
        self.wait()
        square = m.Square(side_length=4, color=m.BLUE, fill_opacity=0.8)
        shape.become(square)
        self.wait(2)  # the same mobject: its updater still turns it

使 mobject 立即看起来像另一个,同时保持自身:场景中 同一个对象,保留其更新器。

两个家族首先被赋予相同的结构;然后每个成员取其 对应成员的点和颜色、不透明度、描边宽度、光泽和 纹理(其连接和端帽样式及 3D 着色保持自身,除非它们 相同)。使用 match_* 标志或 stretch 时,mobject 的拷贝首先被 拟合到此 mobject。

mobject.become(mobject, match_height=False, match_width=False, match_depth=False, match_center=False, stretch=False)
mobject

要看起来像的 mobject。

match_height

拷贝是否先按比例缩放到此 mobject 的高度。

match_width

是否先缩放到此 mobject 的宽度。

match_depth

是否先缩放到此 mobject 的深度。

match_center

是否先移到此 mobject 的中心。

stretch

是否先拉伸到此 mobject 的宽度、高度和 深度,替代 match_* 缩放。

源码

manimgx\mobject.py

def become(
    self,
    mobject: "Mobject",
    match_height: bool = False,
    match_width: bool = False,
    match_depth: bool = False,
    match_center: bool = False,
    stretch: bool = False,
) -> Self:
    """使 mobject 立即看起来像另一个,同时保持自身:场景中
    同一个对象,保留其更新器。

    两个家族首先被赋予相同的结构;然后每个成员取其
    对应成员的点和颜色、不透明度、描边宽度、光泽和
    纹理(其连接和端帽样式及 3D 着色保持自身,除非它们
    相同)。使用 `match_*` 标志或 `stretch` 时,`mobject` 的拷贝首先被
    拟合到此 mobject。

    Args:
        mobject: 要看起来像的 mobject。
        match_height: 拷贝是否先按比例缩放到此
            mobject 的高度。
        match_width: 是否先缩放到此 mobject 的宽度。
        match_depth: 是否先缩放到此 mobject 的深度。
        match_center: 是否先移到此 mobject 的中心。
        stretch: 是否先拉伸到此 mobject 的宽度、高度和
            深度,替代 `match_*` 缩放。

    Examples:
        ```python
        import manimgx as m


        class MobjectBecomeExample(m.Scene):
            def construct(self) -> None:
                shape = m.Circle(radius=2, color=m.RED, fill_opacity=0.8)
                shape.add_updater(lambda mob, dt: mob.rotate(dt))
                self.add(shape)
                self.wait()
                square = m.Square(side_length=4, color=m.BLUE, fill_opacity=0.8)
                shape.become(square)
                self.wait(2)  # the same mobject: its updater still turns it
        ```
    """
    if stretch or match_height or match_width or match_depth or match_center:
        mobject = mobject.copy()
        for dim, match in ((1, match_height), (0, match_width), (2, match_depth)):
            if stretch or match:
                mobject.rescale_to_fit(self.length_over_dim(dim), dim, stretch)
        if match_center:
            mobject.move_to(self.get_center())
    self.align_data(mobject, skip_point_alignment=True)
    for sm1, sm2 in zip(self.get_family(), mobject.get_family(), strict=True):
        sm1._geometry = sm2._geometry  # immutable: shared, not copied
        sm1._take_shape(sm2)
        a, b = sm1.paint, sm2.paint
        # (the rest — colors, widths, windows, dashes — `mixed` takes from b at 1); a
        # picture is what the mobject shows, so it becomes b's too (a texture is an array
        # or a camera: compared by identity)
        same = (a.joint, a.cap, a.shade_in_3d) == (b.joint, b.cap, b.shade_in_3d)
        sm1.paint = (  # a value: shared, not copied
            b
            if same and a.texture is b.texture
            else a.mixed(a, b, 1).but(texture=b.texture)
        )
    return self

match_points

取另一个 mobject 的形状:其点,按家族顺序逐成员取; 此 mobject 的样式保持自身。

较短家族末尾之后的成员保持原样。

mobject.match_points(mobject)
mobject

要取其形状的 mobject。

源码

manimgx\mobject.py

def match_points(self, mobject: "Mobject") -> Self:
    """取另一个 mobject 的形状:其点,按家族顺序逐成员取;
    此 mobject 的样式保持自身。

    较短家族末尾之后的成员保持原样。

    Args:
        mobject: 要取其形状的 mobject。
    """
    for sm1, sm2 in zip(self.get_family(), mobject.get_family(), strict=False):
        sm1._geometry = sm2._geometry
        sm1._take_shape(sm2)
    return self