跳转至

位置

影片代码
import manimgx as m


class PositionsHero(m.Scene):
    def construct(self) -> None:
        plane = m.NumberPlane(
            background_line_style={"stroke_color": m.GREY_D, "stroke_width": 1},
            axis_config={"stroke_color": m.GREY_B, "stroke_width": 2},
        )
        self.add(plane)
        names = ["ORIGIN", "UP", "DOWN", "LEFT", "RIGHT", "UL", "UR", "DL", "DR"]
        points = [m.ORIGIN, m.UP, m.DOWN, m.LEFT, m.RIGHT, m.UL, m.UR, m.DL, m.DR]
        dots = m.VGroup()
        labels = m.VGroup()
        for name, point in zip(names, points, strict=True):
            dot = m.Dot(2.5 * point, radius=0.12, color=m.YELLOW)
            label = m.Text(name, font="monospace", font_size=30)
            label.next_to(dot, m.DOWN, buff=0.2)
            label.add_background_rectangle(opacity=1, buff=0.05)
            dots.add(dot)
            labels.add(label)
        self.play(
            m.LaggedStart(*[m.GrowFromCenter(dot) for dot in dots], lag_ratio=0.1)
        )
        self.play(m.FadeIn(labels))
        self.wait()

每个 mobject 在画面中都有一个位置。位置是一个点,即三个数字 [x, y, z],以画面中心为 原点按单位度量。x 向右增长,y 向上增长,z 向屏幕外、朝向观察者增长。画面的短边为 8 个 单位:宽屏视频的 y 范围为 −4 到 4,x 范围约为 −7.1 到 7.1。

mobject 的位置是其包围盒:沿坐标轴方向围绕其绘制内容的最小盒子。其中心、边缘和角都是 该盒子的点。以下方法读取这些点,并移动 mobject 使其中某个点到达你指定的位置。每个方法 都会连同移动 mobject 的组成部分,并返回该 mobject,因此调用可以链式进行: m.Text("Title").scale(1.5).to_edge(m.UP)。

方向

轴向是单位向量;对角线方向是它们的和。将方向乘以一个数,或将多个方向相加,可以构造其他 点:3 * m.RIGHT + 2 * m.UP 即为 [3, 2, 0]。

m.ORIGIN

场景的中心:(0, 0, 0)。

m.UP

向上一个单位:(0, 1, 0)。

m.DOWN

向下一个单位:(0, −1, 0)。

m.LEFT

向左一个单位:(−1, 0, 0)。

m.RIGHT

向右一个单位:(1, 0, 0)。

m.UL

向上且向左,朝向左上角:(−1, 1, 0)。

m.UR

向上且向右,朝向右上角:(1, 1, 0)。

m.DL

向下且向左,朝向左下角:(−1, −1, 0)。

m.DR

向下且向右,朝向右下角:(1, −1, 0)。

m.IN

向屏幕内一个单位,远离观察者:(0, 0, −1)。

m.OUT

向屏幕外一个单位,朝向观察者:(0, 0, 1)。

m.X_AXIS

x 轴的方向:(1, 0, 0)。

m.Y_AXIS

y 轴的方向:(0, 1, 0)。

m.Z_AXIS

z 轴的方向,指向屏幕外:(0, 0, 1)。

mobject 的位置

以下每个方法读取 mobject 包围盒的一个点,或其某个坐标。

get_center

mobject 边界框的中心。

mobject.get_center()

返回 该点,以场景坐标表示。

源码

manimgx\mobject.py

def get_center(self) -> Point3D:
    """mobject 边界框的中心。

    Returns:
        该点,以场景坐标表示。
    """
    return self.get_critical_point(np.zeros(self.dim))

get_top

mobject 边界框顶边的中点。

mobject.get_top()

返回 该点,以场景坐标表示。

源码

manimgx\mobject.py

def get_top(self) -> Point3D:
    """mobject 边界框顶边的中点。

    Returns:
        该点,以场景坐标表示。
    """
    return self.get_critical_point(UP)

get_bottom

mobject 边界框底边的中点。

mobject.get_bottom()

返回 该点,以场景坐标表示。

源码

manimgx\mobject.py

def get_bottom(self) -> Point3D:
    """mobject 边界框底边的中点。

    Returns:
        该点,以场景坐标表示。
    """
    return self.get_critical_point(DOWN)

get_left

mobject 边界框左边的中点。

mobject.get_left()

返回 该点,以场景坐标表示。

源码

manimgx\mobject.py

def get_left(self) -> Point3D:
    """mobject 边界框左边的中点。

    Returns:
        该点,以场景坐标表示。
    """
    return self.get_critical_point(LEFT)

get_right

mobject 边界框右边的中点。

mobject.get_right()

返回 该点,以场景坐标表示。

源码

manimgx\mobject.py

def get_right(self) -> Point3D:
    """mobject 边界框右边的中点。

    Returns:
        该点,以场景坐标表示。
    """
    return self.get_critical_point(RIGHT)

get_corner

mobject 边界框的角点,以方向命名(get_corner(UR)): get_critical_point 的另一个名称。

get_critical_point

mobject 边界框上的一个点,以方向命名:其中心、 一条边的中点或一个角点。

每个坐标在方向分量为负时取边界框的最低值,为正时取 最高值,为 0 时取中值:UR 命名右上角,UP 命名顶边中点, ORIGIN 命名中心。该边界框为 boundary_box,围绕 mobject 绘制的内容: 路径的曲线本身,而非其控制点。

mobject.get_critical_point(direction)
direction

方向,如 UP、UR 或 ORIGIN。

返回 该点,以场景坐标表示;若 mobject 无点则为原点。

源码

manimgx\mobject.py

def get_critical_point(self, direction: Vector3DLike) -> Point3D:
    """mobject 边界框上的一个点,以方向命名:其中心、
    一条边的中点或一个角点。

    每个坐标在方向分量为负时取边界框的最低值,为正时取
    最高值,为 0 时取中值:UR 命名右上角,UP 命名顶边中点,
    ORIGIN 命名中心。该边界框为
    [boundary_box][manimgx.Mobject.boundary_box],围绕 mobject 绘制的内容:
    路径的曲线本身,而非其控制点。

    Args:
        direction: 方向,如 UP、UR 或 ORIGIN。

    Returns:
        该点,以场景坐标表示;若 mobject 无点则为原点。
    """
    box = self.boundary_box()
    if box is None:
        return np.zeros(self.dim)
    lo, hi = box.tolist()
    return np.array(
        [
            lo[k] if d < 0 else hi[k] if d > 0 else (lo[k] + hi[k]) / 2
            for k, d in enumerate(np.asarray(direction).tolist()[: self.dim])
        ]
    )

get_boundary_point

mobject 在某方向上最远的点:在其绘制经过的点中, 沿 direction 方向最远的那个。

与 get_critical_point 不同,它是 这些点之一(对于路径,为其某条曲线的端点),而非 mobject 边界框上的点。

mobject.get_boundary_point(direction)

返回 该点,以场景坐标表示。

源码

manimgx\mobject.py

def get_boundary_point(self, direction: Vector3DLike) -> Point3D:
    """mobject 在某方向上最远的点:在其绘制经过的点中,
    沿 `direction` 方向最远的那个。

    与 [get_critical_point][manimgx.Mobject.get_critical_point] 不同,它是
    这些点之一(对于路径,为其某条曲线的端点),而非 mobject
    边界框上的点。

    Returns:
        该点,以场景坐标表示。
    """
    points = self.get_points_defining_boundary()
    return points[np.argmax(points @ np.asarray(direction))]

get_x

mobject 中心的 x 坐标,或其边界框某边的 x 坐标。

mobject.get_x(direction=ORIGIN)
direction

LEFT 表示其左边,RIGHT 表示其右边;ORIGIN (默认)表示其中心。

源码

manimgx\mobject.py

def get_x(self, direction: Vector3DLike = ORIGIN) -> float:
    """mobject 中心的 x 坐标,或其边界框某边的 x 坐标。

    Args:
        direction: LEFT 表示其左边,RIGHT 表示其右边;ORIGIN
            (默认)表示其中心。
    """
    return self.get_coord(0, direction)

get_y

mobject 中心的 y 坐标,或其边界框某边的 y 坐标。

mobject.get_y(direction=ORIGIN)
direction

DOWN 表示其底边,UP 表示其顶边;ORIGIN(默认) 表示其中心。

源码

manimgx\mobject.py

def get_y(self, direction: Vector3DLike = ORIGIN) -> float:
    """mobject 中心的 y 坐标,或其边界框某边的 y 坐标。

    Args:
        direction: DOWN 表示其底边,UP 表示其顶边;ORIGIN(默认)
            表示其中心。
    """
    return self.get_coord(1, direction)

get_z

mobject 中心的 z 坐标,或其边界框某面的 z 坐标。

mobject.get_z(direction=ORIGIN)
direction

IN 表示其最低 z,OUT 表示其最高 z;ORIGIN(默认)表示 其中心。

源码

manimgx\mobject.py

def get_z(self, direction: Vector3DLike = ORIGIN) -> float:
    """mobject 中心的 z 坐标,或其边界框某面的 z 坐标。

    Args:
        direction: IN 表示其最低 z,OUT 表示其最高 z;ORIGIN(默认)表示
            其中心。
    """
    return self.get_coord(2, direction)

boundary_box

mobject 的边界框:围绕其家族绘制内容的最小、与坐标轴对齐的框。

路径由其曲线本身测量,而非其控制点(曲线在转折处超出其锚点, 其控制柄超出曲线);其他类型由其点测量。描边宽度不计入。它是 所有度量所读取的唯一边界框:width 和 height,角点、边和中心 (get_critical_point),get_x 和 get_y, 对齐和布局,以及颜色渐变的范围。它合并家族各成员的边界框, 因此一个成员在任何持有它的组中跨度相同。

mobject.boundary_box()

返回 一个 2 × 3 数组,最低 (x, y, z) 然后最高;若家族无点则为 None。

源码

manimgx\mobject.py

def boundary_box(self) -> np.ndarray | None:
    """mobject 的边界框:围绕其家族绘制内容的最小、与坐标轴对齐的框。

    路径由其曲线本身测量,而非其控制点(曲线在转折处超出其锚点,
    其控制柄超出曲线);其他类型由其点测量。描边宽度不计入。它是
    所有度量所读取的唯一边界框:[width][manimgx.Mobject.width] 和
    [height][manimgx.Mobject.height],角点、边和中心
    ([get_critical_point][manimgx.Mobject.get_critical_point]),`get_x` 和 `get_y`,
    对齐和布局,以及颜色渐变的范围。它合并家族各成员的边界框,
    因此一个成员在任何持有它的组中跨度相同。

    Returns:
        一个 2 × 3 数组,最低 (x, y, z) 然后最高;若家族无点则为
        None。
    """
    return _family_box(self.get_family())

移动 mobject

move_to

代码
import manimgx as m


class MobjectMoveToExample(m.Scene):
    def construct(self) -> None:
        dot = m.Dot([3, 1.5, 0], radius=0.15, color=m.YELLOW)
        origin = m.Dot(radius=0.15, color=m.RED)
        square = m.Square(color=m.BLUE, fill_opacity=0.5).shift(4 * m.LEFT)
        self.add(dot, origin, square)
        self.play(square.animate.move_to(dot))
        self.play(square.animate.move_to(m.ORIGIN, aligned_edge=m.DL))

将 mobject 移动到一个点,或到另一个 mobject 上:其中心,或 其边界框上 aligned_edge 所命名的点,到达该处。

mobject.move_to(point_or_mobject, aligned_edge=ORIGIN, coor_mask=(1, 1, 1))
point_or_mobject

一个点,以场景坐标表示,或一个 mobject:此时为 其边界框上 aligned_edge 所命名的点(默认为其中心)。

aligned_edge

到达该处的 mobject 上的点,以 方向命名:ORIGIN(默认)表示其中心,DL 表示其左下 角。

coor_mask

哪些坐标改变:mobject 沿其移动的每个轴为 1, 保持不变的轴为 0((1, 0, 0) 仅水平移动)。

源码

manimgx\mobject.py

def move_to(
    self,
    point_or_mobject: "Point3DLike | Mobject",
    aligned_edge: Vector3DLike = ORIGIN,
    coor_mask: Vector3DLike = (1, 1, 1),
) -> Self:
    """将 mobject 移动到一个点,或到另一个 mobject 上:其中心,或
    其边界框上 `aligned_edge` 所命名的点,到达该处。

    Args:
        point_or_mobject: 一个点,以场景坐标表示,或一个 mobject:此时为
            其边界框上 `aligned_edge` 所命名的点(默认为其中心)。
        aligned_edge: 到达该处的 mobject 上的点,以
            方向命名:ORIGIN(默认)表示其中心,DL 表示其左下
            角。
        coor_mask: 哪些坐标改变:mobject 沿其移动的每个轴为 1,
            保持不变的轴为 0(`(1, 0, 0)` 仅水平移动)。

    Examples:
        ```python
        import manimgx as m


        class MobjectMoveToExample(m.Scene):
            def construct(self) -> None:
                dot = m.Dot([3, 1.5, 0], radius=0.15, color=m.YELLOW)
                origin = m.Dot(radius=0.15, color=m.RED)
                square = m.Square(color=m.BLUE, fill_opacity=0.5).shift(4 * m.LEFT)
                self.add(dot, origin, square)
                self.play(square.animate.move_to(dot))
                self.play(square.animate.move_to(m.ORIGIN, aligned_edge=m.DL))
        ```
    """
    if isinstance(point_or_mobject, Mobject):
        target = point_or_mobject.get_critical_point(aligned_edge)
    else:
        target = np.asarray(point_or_mobject, dtype=float)
    return self.shift(
        (target - self.get_critical_point(aligned_edge)) * np.asarray(coor_mask)
    )

shift

代码
import manimgx as m


class MobjectShiftExample(m.Scene):
    def construct(self) -> None:
        square = m.Square(color=m.BLUE, fill_opacity=0.5)
        self.add(square)
        self.play(square.animate.shift(4 * m.LEFT))
        self.play(square.animate.shift(2 * m.UP, 8 * m.RIGHT))

将 mobject 及其整个家族按一个向量移动。

mobject.shift(*vectors)
*vectors

向量,以场景单位表示(2 * RIGHT 将其向右移动两个单位); 多个向量会相加为一个。

源码

manimgx\mobject.py

def shift(self, *vectors: Vector3DLike) -> Self:
    """将 mobject 及其整个家族按一个向量移动。

    Args:
        *vectors: 向量,以场景单位表示(`2 * RIGHT` 将其向右移动两个单位);
            多个向量会相加为一个。

    Examples:
        ```python
        import manimgx as m


        class MobjectShiftExample(m.Scene):
            def construct(self) -> None:
                square = m.Square(color=m.BLUE, fill_opacity=0.5)
                self.add(square)
                self.play(square.animate.shift(4 * m.LEFT))
                self.play(square.animate.shift(2 * m.UP, 8 * m.RIGHT))
        ```
    """
    # one vector (a (1, 3) array too, as CE takes it)
    vector = vectors[0] if len(vectors) == 1 else reduce(np.add, vectors)
    total = np.asarray(vector, dtype=float).reshape(-1)
    for mob in self.family_members_with_points():
        mob._geometry = mob._geometry.translated(total)
    return self

center

移动 mobject 使其边界框的中心位于原点。

mobject.center()
源码

manimgx\mobject.py

def center(self) -> Self:
    """移动 mobject 使其边界框的中心位于原点。"""
    return self.shift(-self.get_center())

set_x

左右移动 mobject 使其中心或边界框的某边 位于 x。

mobject.set_x(x, direction=ORIGIN)
x

x 坐标,以场景单位表示。

direction

LEFT 放置其左边,RIGHT 放置其右边;ORIGIN (默认)放置其中心。

源码

manimgx\mobject.py

def set_x(self, x: float, direction: Vector3DLike = ORIGIN) -> Self:
    """左右移动 mobject 使其中心或边界框的某边
    位于 `x`。

    Args:
        x: x 坐标,以场景单位表示。
        direction: LEFT 放置其左边,RIGHT 放置其右边;ORIGIN
            (默认)放置其中心。
    """
    return self.set_coord(x, 0, direction)

set_y

上下移动 mobject 使其中心或边界框的某边位于 y。

mobject.set_y(y, direction=ORIGIN)
y

y 坐标,以场景单位表示。

direction

DOWN 放置其底边,UP 放置其顶边;ORIGIN(默认) 放置其中心。

源码

manimgx\mobject.py

def set_y(self, y: float, direction: Vector3DLike = ORIGIN) -> Self:
    """上下移动 mobject 使其中心或边界框的某边位于
    `y`。

    Args:
        y: y 坐标,以场景单位表示。
        direction: DOWN 放置其底边,UP 放置其顶边;ORIGIN(默认)
            放置其中心。
    """
    return self.set_coord(y, 1, direction)

set_z

内外移动 mobject 使其中心或边界框的某面位于 z。

mobject.set_z(z, direction=ORIGIN)
z

z 坐标,以场景单位表示。

direction

IN 放置其最低 z,OUT 放置其最高 z;ORIGIN(默认)放置其 中心。

源码

manimgx\mobject.py

def set_z(self, z: float, direction: Vector3DLike = ORIGIN) -> Self:
    """内外移动 mobject 使其中心或边界框的某面位于
    `z`。

    Args:
        z: z 坐标,以场景单位表示。
        direction: IN 放置其最低 z,OUT 放置其最高 z;ORIGIN(默认)放置其
            中心。
    """
    return self.set_coord(z, 2, direction)

依据另一个 mobject 放置

两个 mobject 之间的间距称为 buff。以下是其常用尺寸。

m.SMALL_BUFF

小间距,以场景单位表示:0.1。

m.MED_SMALL_BUFF

中偏小间距,以场景单位表示:0.25,是相邻排列的 mobject 之间的默认间距。

m.MED_LARGE_BUFF

中偏大间距,以场景单位表示:0.5,是 mobject 与画面边缘之间的默认间距。

m.LARGE_BUFF

大间距,以场景单位表示:1。

m.DEFAULT_MOBJECT_TO_MOBJECT_BUFFER

相邻排列的 mobject 之间的默认间距,以场景单位表示:0.25 (next_to、arrange)。

m.DEFAULT_MOBJECT_TO_EDGE_BUFFER

mobject 默认与画面边缘保持的间距,以场景单位表示:0.5 (to_edge、to_corner)。

next_to

MobjectNextToExample
代码
import manimgx as m


class MobjectNextToExample(m.Scene):
    def construct(self) -> None:
        square = m.Square(side_length=3, color=m.BLUE, fill_opacity=0.5)
        right = m.Text("RIGHT").next_to(square, m.RIGHT)
        up = m.Text("UP, aligned LEFT").next_to(
            square, m.UP, aligned_edge=m.LEFT
        )
        down = m.Text("DOWN, buff=1").next_to(square, m.DOWN, buff=1)
        dot = m.Dot(radius=0.2, color=m.YELLOW)
        dot.next_to(square, m.UL, buff=0)
        self.add(square, right, up, down, dot)

将 mobject 放在另一个旁边,或一个点旁边,沿某方向 间隔 buff。

mobject 沿 direction 反方向面向的边到达另一个沿该方向面向的边, 再远 buff:使用 RIGHT 时,其左边中点到达另一个右边中点 右侧 buff 处。对角方向(UR)使角对角。

mobject.next_to(mobject_or_point, direction=RIGHT, buff=DEFAULT_MOBJECT_TO_MOBJECT_BUFFER, aligned_edge=ORIGIN, submobject_to_align=None, index_of_submobject_to_align=None, coor_mask=(1, 1, 1))
mobject_or_point

要放在其旁边的 mobject(其边界框)或点。

direction

方向:RIGHT(默认)、UP、DL、…

buff

沿 direction 的间距,以场景单位表示(默认 0.25)。

aligned_edge

两个对象在它们相遇侧旁对齐所沿的边, 以方向命名:当方向为 RIGHT 和 UP 时, 它们的顶部对齐(默认 ORIGIN:居中)。

submobject_to_align

要放在另一对象旁边的 mobject 的一部分,代替 其位置;其余部分随其移动。

index_of_submobject_to_align

每个 mobject 中要对齐部分的索引: 此对象的部分放在另一对象的部分旁边。

coor_mask

哪些坐标改变:mobject 沿其移动的每个轴为 1, 保持不变的轴为 0。

源码

manimgx\mobject.py

def next_to(
    self,
    mobject_or_point: "Mobject | Point3DLike",
    direction: Vector3DLike = RIGHT,
    buff: float = DEFAULT_MOBJECT_TO_MOBJECT_BUFFER,
    aligned_edge: Vector3DLike = ORIGIN,
    submobject_to_align: "Mobject | None" = None,
    index_of_submobject_to_align: int | None = None,
    coor_mask: Vector3DLike = (1, 1, 1),
) -> Self:
    """将 mobject 放在另一个旁边,或一个点旁边,沿某方向
    间隔 `buff`。

    mobject 沿 `direction` 反方向面向的边到达另一个沿该方向面向的边,
    再远 `buff`:使用 RIGHT 时,其左边中点到达另一个右边中点
    右侧 `buff` 处。对角方向(UR)使角对角。

    Args:
        mobject_or_point: 要放在其旁边的 mobject(其边界框)或点。
        direction: 方向:RIGHT(默认)、UP、DL、…
        buff: 沿 `direction` 的间距,以场景单位表示(默认 0.25)。
        aligned_edge: 两个对象在它们相遇侧旁对齐所沿的边,
            以方向命名:当方向为 RIGHT 和 UP 时,
            它们的顶部对齐(默认 ORIGIN:居中)。
        submobject_to_align: 要放在另一对象旁边的 mobject 的一部分,代替
            其位置;其余部分随其移动。
        index_of_submobject_to_align: 每个 mobject 中要对齐部分的索引:
            此对象的部分放在另一对象的部分旁边。
        coor_mask: 哪些坐标改变:mobject 沿其移动的每个轴为 1,
            保持不变的轴为 0。

    Examples:
        ```python
        import manimgx as m


        class MobjectNextToExample(m.Scene):
            def construct(self) -> None:
                square = m.Square(side_length=3, color=m.BLUE, fill_opacity=0.5)
                right = m.Text("RIGHT").next_to(square, m.RIGHT)
                up = m.Text("UP, aligned LEFT").next_to(
                    square, m.UP, aligned_edge=m.LEFT
                )
                down = m.Text("DOWN, buff=1").next_to(square, m.DOWN, buff=1)
                dot = m.Dot(radius=0.2, color=m.YELLOW)
                dot.next_to(square, m.UL, buff=0)
                self.add(square, right, up, down, dot)
        ```
    """
    d, edge = (
        np.asarray(direction, dtype=float),
        np.asarray(aligned_edge, dtype=float),
    )
    if isinstance(mobject_or_point, Mobject):
        target_aligner = (
            mobject_or_point
            if index_of_submobject_to_align is None
            else mobject_or_point[index_of_submobject_to_align]
        )
        target = target_aligner.get_critical_point(edge + d)
    else:
        target = np.asarray(mobject_or_point, dtype=float)
    if submobject_to_align is not None:
        aligner: Mobject = submobject_to_align
    elif index_of_submobject_to_align is not None:
        aligner = self[index_of_submobject_to_align]
    else:
        aligner = self
    point_to_align = aligner.get_critical_point(edge - d)
    return self.shift((target - point_to_align + buff * d) * np.asarray(coor_mask))

align_to

代码
import manimgx as m


class MobjectAlignToExample(m.Scene):
    def construct(self) -> None:
        floor = m.Line(6 * m.LEFT, 6 * m.RIGHT).shift(3 * m.DOWN)
        shapes = m.VGroup(
            m.Circle(color=m.BLUE),
            m.Square(side_length=3, color=m.GREEN),
            m.Triangle(color=m.YELLOW),
            m.Star(color=m.RED),
        ).arrange(buff=1)
        self.add(floor, shapes)
        self.play(*(s.animate.align_to(floor, m.DOWN) for s in shapes))

移动 mobject 使其某侧与另一个对齐:在 direction 不为 0 的每个轴上, 其边界框在该方向上的侧到达另一个对应侧的位置。

align_to(other, UP) 将其顶部放到另一个顶部的高度,仅上下移动; align_to(other, UL) 同时对齐顶部和左侧。 使用默认 ORIGIN 时,什么也不移动。

mobject.align_to(mobject_or_point, direction=ORIGIN)
mobject_or_point

要与之对齐的 mobject(其边界框上 direction 命名的侧),或一个点。

direction

侧,以方向命名:UP、DL、…

源码

manimgx\mobject.py

def align_to(
    self,
    mobject_or_point: "Mobject | Point3DLike",
    direction: Vector3DLike = ORIGIN,
) -> Self:
    """移动 mobject 使其某侧与另一个对齐:在 `direction` 不为 0 的每个轴上,
    其边界框在该方向上的侧到达另一个对应侧的位置。

    `align_to(other, UP)` 将其顶部放到另一个顶部的高度,仅上下移动;
    `align_to(other, UL)` 同时对齐顶部和左侧。
    使用默认 ORIGIN 时,什么也不移动。

    Args:
        mobject_or_point: 要与之对齐的 mobject(其边界框上 `direction`
            命名的侧),或一个点。
        direction: 侧,以方向命名:UP、DL、…

    Examples:
        ```python
        import manimgx as m


        class MobjectAlignToExample(m.Scene):
            def construct(self) -> None:
                floor = m.Line(6 * m.LEFT, 6 * m.RIGHT).shift(3 * m.DOWN)
                shapes = m.VGroup(
                    m.Circle(color=m.BLUE),
                    m.Square(side_length=3, color=m.GREEN),
                    m.Triangle(color=m.YELLOW),
                    m.Star(color=m.RED),
                ).arrange(buff=1)
                self.add(floor, shapes)
                self.play(*(s.animate.align_to(floor, m.DOWN) for s in shapes))
        ```
    """
    point = (
        mobject_or_point.get_critical_point(direction)
        if isinstance(mobject_or_point, Mobject)
        else np.asarray(mobject_or_point)
    )
    for dim in range(self.dim):
        if direction[dim] != 0:
            self.set_coord(point[dim], dim, direction)
    return self

match_x

左右移动 mobject 至另一个的 x:中心,或左右 边对齐。

mobject.match_x(mobject, direction=ORIGIN)
mobject

要匹配的 mobject。

direction

LEFT 对齐左边,RIGHT 对齐右边;ORIGIN (默认)对齐中心。

源码

manimgx\mobject.py

def match_x(self, mobject: "Mobject", direction: Vector3DLike = ORIGIN) -> Self:
    """左右移动 mobject 至另一个的 x:中心,或左右
    边对齐。

    Args:
        mobject: 要匹配的 mobject。
        direction: LEFT 对齐左边,RIGHT 对齐右边;ORIGIN
            (默认)对齐中心。
    """
    return self.match_coord(mobject, 0, direction)

match_y

上下移动 mobject 至另一个的 y:中心,或顶边或底边 对齐。

mobject.match_y(mobject, direction=ORIGIN)
mobject

要匹配的 mobject。

direction

DOWN 对齐底边,UP 对齐顶边;ORIGIN (默认)对齐中心。

源码

manimgx\mobject.py

def match_y(self, mobject: "Mobject", direction: Vector3DLike = ORIGIN) -> Self:
    """上下移动 mobject 至另一个的 y:中心,或顶边或底边
    对齐。

    Args:
        mobject: 要匹配的 mobject。
        direction: DOWN 对齐底边,UP 对齐顶边;ORIGIN
            (默认)对齐中心。
    """
    return self.match_coord(mobject, 1, direction)

match_z

内外移动 mobject 至另一个的 z:中心,或面对齐。

mobject.match_z(mobject, direction=ORIGIN)
mobject

要匹配的 mobject。

direction

IN 对齐最低 z,OUT 对齐最高 z;ORIGIN(默认)对齐 中心。

源码

manimgx\mobject.py

def match_z(self, mobject: "Mobject", direction: Vector3DLike = ORIGIN) -> Self:
    """内外移动 mobject 至另一个的 z:中心,或面对齐。

    Args:
        mobject: 要匹配的 mobject。
        direction: IN 对齐最低 z,OUT 对齐最高 z;ORIGIN(默认)对齐
            中心。
    """
    return self.match_coord(mobject, 2, direction)

在画面中放置 mobject

to_edge

MobjectToEdgeExample
代码
import manimgx as m


class MobjectToEdgeExample(m.Scene):
    def construct(self) -> None:
        top = m.Text("to_edge(UP)").to_edge(m.UP)
        left = m.Text("to_edge(LEFT)").to_edge(m.LEFT)
        circle = m.Circle(color=m.BLUE).shift(2 * m.DOWN)
        circle.to_edge(m.RIGHT, buff=0)
        self.add(top, left, circle)

将 mobject 移到画框的某边,距其 buff 向内;沿该边方向 保持原位。

画框为已配置的画框,以原点为中心,无论相机看向何处。

mobject.to_edge(edge=LEFT, buff=DEFAULT_MOBJECT_TO_EDGE_BUFFER)
edge

边:UP、DOWN、LEFT(默认)或 RIGHT。

buff

mobject 与边之间的边距,以场景单位表示(默认 0.5)。

源码

manimgx\mobject.py

def to_edge(
    self, edge: Vector3DLike = LEFT, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER
) -> Self:
    """将 mobject 移到画框的某边,距其 `buff` 向内;沿该边方向
    保持原位。

    画框为已配置的画框,以原点为中心,无论相机看向何处。

    Args:
        edge: 边:UP、DOWN、LEFT(默认)或 RIGHT。
        buff: mobject 与边之间的边距,以场景单位表示(默认
            0.5)。

    Examples:
        ```python
        import manimgx as m


        class MobjectToEdgeExample(m.Scene):
            def construct(self) -> None:
                top = m.Text("to_edge(UP)").to_edge(m.UP)
                left = m.Text("to_edge(LEFT)").to_edge(m.LEFT)
                circle = m.Circle(color=m.BLUE).shift(2 * m.DOWN)
                circle.to_edge(m.RIGHT, buff=0)
                self.add(top, left, circle)
        ```
    """
    return self.align_on_border(edge, buff)

to_corner

MobjectToCornerExample
代码
import manimgx as m


class MobjectToCornerExample(m.Scene):
    def construct(self) -> None:
        self.add(m.Text("UL").to_corner(m.UL))
        self.add(m.Text("UR, buff=0").to_corner(m.UR, buff=0))
        self.add(m.Circle(color=m.BLUE).to_corner(m.DL))
        self.add(m.Square(color=m.YELLOW).to_corner(m.DR, buff=1))

将 mobject 移入画框的某角,距两条边各 buff 向内。

画框为已配置的画框,以原点为中心,无论相机看向何处。

mobject.to_corner(corner=DL, buff=DEFAULT_MOBJECT_TO_EDGE_BUFFER)
corner

角:UL、UR、DL(默认)或 DR。

buff

mobject 与画框每条边之间的边距,以场景 单位表示(默认 0.5)。

源码

manimgx\mobject.py

def to_corner(
    self, corner: Vector3DLike = DL, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER
) -> Self:
    """将 mobject 移入画框的某角,距两条边各 `buff` 向内。

    画框为已配置的画框,以原点为中心,无论相机看向何处。

    Args:
        corner: 角:UL、UR、DL(默认)或 DR。
        buff: mobject 与画框每条边之间的边距,以场景
            单位表示(默认 0.5)。

    Examples:
        ```python
        import manimgx as m


        class MobjectToCornerExample(m.Scene):
            def construct(self) -> None:
                self.add(m.Text("UL").to_corner(m.UL))
                self.add(m.Text("UR, buff=0").to_corner(m.UR, buff=0))
                self.add(m.Circle(color=m.BLUE).to_corner(m.DL))
                self.add(m.Square(color=m.YELLOW).to_corner(m.DR, buff=1))
        ```
    """
    return self.align_on_border(corner, buff)

shift_onto_screen

将 mobject 移回画框内,当它越过某边或距某边 不足 buff 时:移到该边,向内 buff(参见 to_edge)。

mobject.shift_onto_screen(buff=DEFAULT_MOBJECT_TO_EDGE_BUFFER)
buff

与画框边保持的边距,以场景单位表示(默认 0.5)。

源码

manimgx\mobject.py

def shift_onto_screen(self, buff: float = DEFAULT_MOBJECT_TO_EDGE_BUFFER) -> Self:
    """将 mobject 移回画框内,当它越过某边或距某边
    不足 `buff` 时:移到该边,向内 `buff`(参见
    [to_edge][manimgx.Mobject.to_edge])。

    Args:
        buff: 与画框边保持的边距,以场景单位表示(默认
            0.5)。
    """
    radii = (config.frame_x_radius, config.frame_y_radius)
    for vect in (UP, DOWN, LEFT, RIGHT):
        dim = int(np.argmax(np.abs(vect)))
        if np.dot(self.get_critical_point(vect), vect) > radii[dim] - buff:
            self.to_edge(vect, buff=buff)
    return self

is_off_screen

mobject 是否完全在画框之外:已配置的画框,以原点为中心。

mobject.is_off_screen()

返回 若其边界框完全越过画框某边则为 True。

源码

manimgx\mobject.py

def is_off_screen(self) -> bool:
    """mobject 是否完全在画框之外:已配置的画框,以原点为中心。

    Returns:
        若其边界框完全越过画框某边则为 True。
    """
    x, y = config.frame_x_radius, config.frame_y_radius
    return bool(
        self.get_left()[0] > x
        or self.get_right()[0] < -x
        or self.get_bottom()[1] > y
        or self.get_top()[1] < -y
    )

计算点

点是 NumPy 数组,因此可使用 NumPy 进行计算。以下函数完成场景中常见的操作。

interpolate

从一个值到另一个值之间某分数位置上的值:(1 − alpha)·start + alpha·end。

m.interpolate(start, end, alpha)
start

0 处的值:一个数或一个数组。

end

1 处的值,类型相同。

alpha

从 start 到 end 的距离:0 给出 start,1 给出 end,超出 此范围则沿直线继续延伸;数组则每个元素给一个值。

返回 两者之间的值。

源码

manimgx\drawing\geometry.py

def interpolate[T](start: T, end: T, alpha: float | np.ndarray) -> T:
    """从一个值到另一个值之间某分数位置上的值:(1 − alpha)·start + alpha·end。

    Args:
        start: 0 处的值:一个数或一个数组。
        end: 1 处的值,类型相同。
        alpha: 从 `start` 到 `end` 的距离:0 给出 `start`,1 给出 `end`,超出
            此范围则沿直线继续延伸;数组则每个元素给一个值。

    Returns:
        两者之间的值。
    """
    return (1 - alpha) * start + alpha * end  # pyright: ignore[reportOperatorIssue]  # ty: ignore[unsupported-operator]  # T: numbers, arrays

midpoint

两点之间的中点。

m.midpoint(p1, p2)
p1

一个点。

p2

另一个点。

返回 两点之间的点。

源码

manimgx\drawing\geometry.py

def midpoint(p1: Point3D, p2: Point3D) -> Point3D:
    """两点之间的中点。

    Args:
        p1: 一个点。
        p2: 另一个点。

    Returns:
        两点之间的点。
    """
    return (np.asarray(p1) + np.asarray(p2)) / 2

normalize

向量方向上的单位向量。

m.normalize(vect)
vect

该向量。

返回 该向量除以其长度;零向量保持为零。

源码

manimgx\drawing\geometry.py

def normalize(vect: Vector3DLike) -> Vector3D:
    """向量方向上的单位向量。

    Args:
        vect: 该向量。

    Returns:
        该向量除以其长度;零向量保持为零。
    """
    v = np.asarray(vect, dtype=float)
    norm = np.linalg.norm(v)
    return v / norm if norm > 0 else np.zeros(len(v))

rotate_vector

绕一条过原点的轴旋转后的向量。

m.rotate_vector(vector, angle, axis=_OUT)
vector

该向量;二维向量的 z 取为 0。

angle

角度,以弧度为单位,从 axis 顶端看去为逆时针方向。

axis

该轴的方向。

返回 旋转后的向量。

源码

manimgx\drawing\geometry.py

def rotate_vector(
    vector: Vector3DLike, angle: float, axis: Vector3DLike = _OUT
) -> Vector3D:
    """绕一条过原点的轴旋转后的向量。

    Args:
        vector: 该向量;二维向量的 z 取为 0。
        angle: 角度,以弧度为单位,从 `axis` 顶端看去为逆时针方向。
        axis: 该轴的方向。

    Returns:
        旋转后的向量。
    """
    v = np.asarray(vector, dtype=float)
    if len(v) == 2:
        v = np.append(v, 0)
    return rotation_matrix(angle, axis) @ v

angle_of_vector

向量在 xy 平面中相对于 x 轴逆时针方向的角度(其 z 被忽略)。

m.angle_of_vector(vector)

返回 角度,以弧度为单位,从 −π 到 π。

源码

manimgx\drawing\geometry.py

def angle_of_vector(vector: Vector3DLike) -> float:
    """向量在 xy 平面中相对于 x 轴逆时针方向的角度(其 z 被忽略)。

    Returns:
        角度,以弧度为单位,从 −π 到 π。
    """
    v = np.asarray(vector)
    return float(np.angle(complex(v[0], v[1])))

angle_between_vectors

两个向量之间的夹角。

m.angle_between_vectors(v1, v2)
v1

一个向量。

v2

另一个向量。

返回 角度,以弧度为单位,从 0 到 π。

源码

manimgx\drawing\geometry.py

def angle_between_vectors(v1: Vector3DLike, v2: Vector3DLike) -> float:
    """两个向量之间的夹角。

    Args:
        v1: 一个向量。
        v2: 另一个向量。

    Returns:
        角度,以弧度为单位,从 0 到 π。
    """
    a, b = normalize(v1), normalize(v2)
    return float(2 * np.arctan2(np.linalg.norm(a - b), np.linalg.norm(a + b)))

line_intersection

xy 平面内两条直线的交点,每条直线由两个点确定(其 z 被忽略)。

若两直线平行则引发异常。

m.line_intersection(line1, line2)
line1

第一条直线上的两个点。

line2

第二条直线上的两个点。

返回 交点,其 z 为 0。

源码

manimgx\drawing\geometry.py

def line_intersection(line1: Point3DLike_Array, line2: Point3DLike_Array) -> Point3D:
    """xy 平面内两条直线的交点,每条直线由两个点确定(其 z 被忽略)。

    若两直线平行则引发异常。

    Args:
        line1: 第一条直线上的两个点。
        line2: 第二条直线上的两个点。

    Returns:
        交点,其 z 为 0。
    """
    padded = [
        np.pad(np.array(line)[:, :2], ((0, 0), (0, 1)), constant_values=1)
        for line in (line1, line2)
    ]
    l1, l2 = (np.cross(*p) for p in padded)
    x, y, z = np.cross(l1, l2)
    if z == 0:
        raise ValueError(
            "The lines are parallel, there is no unique intersection point."
        )
    return np.array([x / z, y / z, 0])

perpendicular_bisector

线段垂直平分线上的两个点,位于垂直于某向量的平面内。

m.perpendicular_bisector(line, norm_vector=_OUT)
line

线段的两个端点。

norm_vector

平分线所在平面的法向量。

返回 线段中点加上和减去其方向与 norm_vector 的叉积;对于单位法向量, 每个点距中点一个线段长度。

源码

manimgx\drawing\geometry.py

def perpendicular_bisector(
    line: Point3DLike_Array, norm_vector: Vector3DLike = _OUT
) -> Point3D_Array:
    """线段垂直平分线上的两个点,位于垂直于某向量的平面内。

    Args:
        line: 线段的两个端点。
        norm_vector: 平分线所在平面的法向量。

    Returns:
        线段中点加上和减去其方向与 `norm_vector` 的叉积;对于单位法向量,
        每个点距中点一个线段长度。
    """
    p1, p2 = np.asarray(line[0]), np.asarray(line[1])
    direction = np.cross(p1 - p2, np.asarray(norm_vector))
    m = midpoint(p1, p2)
    return np.array([m + direction, m - direction])

polylabel

多边形的不可及极点:多边形内部离其边最远的点,即标签最佳放置之处。

m.polylabel(rings, precision=0.01)
rings

多边形的各环,每个闭合(首顶点在末尾重复):先是外轮廓,然后是 任意孔洞。其 x 和 y 计数。

precision

所找到的点在距边的距离上至多离最优点多远,以场景单位表示。

返回 所找到的单元,其 c 为该点(x 和 y),d 为其到最近边的距离。

源码

manimgx\drawing\geometry.py

def polylabel(rings: Sequence[Point3DLike_Array], precision: float = 0.01) -> Cell:
    """多边形的不可及极点:多边形内部离其边最远的点,即标签最佳放置之处。

    Args:
        rings: 多边形的各环,每个闭合(首顶点在末尾重复):先是外轮廓,然后是
            任意孔洞。其 x 和 y 计数。
        precision: 所找到的点在距边的距离上至多离最优点多远,以场景单位表示。

    Returns:
        所找到的单元,其 `c` 为该点(x 和 y),`d` 为其到最近边的距离。
    """
    np_rings: list[Point2D_Array] = [np.asarray(ring)[:, :2] for ring in rings]
    polygon = Polygon(np_rings)
    mins = np.min(polygon.array, axis=0)
    maxs = np.max(polygon.array, axis=0)
    dims = maxs - mins
    s = np.min(dims)
    h = s / 2.0
    queue: list[Cell] = []
    xv, yv = np.meshgrid(np.arange(mins[0], maxs[0], s), np.arange(mins[1], maxs[1], s))
    for corner in np.vstack([xv.ravel(), yv.ravel()]).T:
        heapq.heappush(queue, Cell(corner + h, h, polygon))
    best = Cell(polygon.centroid, 0, polygon)
    bbox = Cell(mins + dims / 2, 0, polygon)
    if bbox.d > best.d:
        best = bbox
    # No inscribed circle is wider than the bounding box.
    if best.d >= h:
        return best
    directions = np.array([[-1, -1], [1, -1], [-1, 1], [1, 1]])
    while queue:
        cell = heapq.heappop(queue)
        if cell.d > best.d:
            best = cell
        if cell.p - best.d > precision:
            h = cell.h / 2.0
            offsets = cell.c + directions * h
            for offset in offsets:
                heapq.heappush(queue, Cell(offset, h, polygon))
    return best

inverse_interpolate

某值从一个值到另一个值之间的距离: interpolate 的逆。

m.inverse_interpolate(start, end, value)
start

0 处的值。

end

1 处的值。

返回 分数,(value − start) / (end − start)。

源码

manimgx\drawing\geometry.py

def inverse_interpolate(start: float, end: float, value: float) -> float:
    """某值从一个值到另一个值之间的距离:
    [interpolate][manimgx.interpolate] 的逆。

    Args:
        start: 0 处的值。
        end: 1 处的值。

    Returns:
        分数,(value − start) / (end − start)。
    """
    return float(np.true_divide(value - start, end - start))