跳转至

相机

影片代码
import manimgx as m


class CameraHero(m.Scene):
    def construct(self) -> None:
        circle = m.Circle(radius=3, color=m.BLUE)
        tangent = m.Line(4 * m.LEFT, 4 * m.RIGHT, color=m.YELLOW).shift(3 * m.UP)
        self.add(circle, tangent)
        frame = self.camera.frame
        self.play(frame.animate.scale(0.25).move_to(3 * m.UP), run_time=2)
        self.wait()
        self.play(frame.animate.scale(4).move_to(m.ORIGIN), run_time=2)

每个场景都有一个相机 self.camera。其画框 self.camera.frame 是一个 mobject:移动它以 平移,缩放它以缩放,并像任何其他 mobject 一样为其制作动画。画框缩小一半,一切看起来就放大 一倍。

相机还设置画面的外观:其背景,以及对于 3D,其曝光、色调映射、泛光和阴影。

Camera

一帧所显示的内容:场景的一个视图,由 mobject 构成,因此移动任何 mobject 即可移动该视图。

其 frame 是场景中的一个矩形,其中心与 尺寸即为该视图:移动它、缩放它或为其添加动画,视图便会平移与缩放。在 三维情形下(ThreeDScene 的相机),它还会绕 帧中心轨道运动,并带有透视:五个值跟踪器 分别持有其角度 phi 与 theta、翻滚角 gamma、zoom 以及 focal_distance(phi_tracker、theta_tracker、gamma_tracker、zoom_tracker 与 focal_distance_tracker),其光源为一个点, light_source。相机仅描述视图:它 从不绘制。

m.Camera(*, three_d=False, frame_width=None, phi=0, theta=-90 * DEGREES, gamma=0, focal_distance=20.0, zoom=1, light_source_start_point=9 * DOWN + 7 * LEFT + 10 * OUT)
three_d

是否以三维方式观察:带透视与深度,从其轨道观察。

frame_width

其帧的宽度,以场景单位表示;None 表示使用配置值 (16:9 时约为 14.2)。高度取配置值;超出视频比例的宽度会拉伸画面。

phi

其视线与 z 轴之间的夹角,以弧度表示:0 表示从正上方俯视 xy 平面。

theta

其绕 z 轴的角度,以弧度表示,从 x 轴逆时针方向计量:在 −90° 时 从 y 轴负侧观察,使 x 轴指向右方。

gamma

其绕自身视线转动的角度,以弧度表示。

focal_distance

其与帧中心的距离,以场景单位表示:越近, 透视越强。

zoom

其放大倍数:2 表示将一切放大两倍显示。

light_source_start_point

光源位置,以场景坐标表示。

源码

manimgx\scene.py

def __init__(
    self,
    *,
    three_d: bool = False,
    frame_width: float | None = None,
    phi: float = 0,
    theta: float = -90 * DEGREES,
    gamma: float = 0,
    focal_distance: float = 20.0,
    zoom: float = 1,
    light_source_start_point: Point3D = 9 * DOWN + 7 * LEFT + 10 * OUT,
) -> None:
    from manimgx.mobject import Point

    self.three_d = three_d
    """相机是否以三维方式观察:带透视与深度,从其轨道观察。"""
    self.frame = _view_rectangle(
        config.frame_height, frame_width or config.frame_width
    )
    """该视图:场景中的一个矩形,以相机所观察的点为中心,宽与高
    等于其所显示的范围。

    移动它、缩放它或为其添加动画即可平移与缩放:
    `self.play(self.camera.frame.animate.scale(0.5))` 放大画面,显示一半的
    内容,但放大两倍。其轮廓不被绘制(描边宽度为 0)。
    """
    self.phi_tracker, self.theta_tracker, self.gamma_tracker = (
        ValueTracker(phi),
        ValueTracker(theta),
        ValueTracker(gamma),
    )
    self.focal_distance_tracker, self.zoom_tracker = (
        ValueTracker(focal_distance),
        ValueTracker(zoom),
    )
    self.light_source = Point(light_source_start_point)
    """光源所在位置:场景中的一个点,为三维着色的 mobject 提供光照
    (`shade_in_3d`)。像任何 mobject 一样移动它。"""
    # the mobjects pinned to the screen, as they were given: what they are made of
    # is read every frame (see `fixed_in_frame_mobjects`)
    self._fixed_in_frame: dict[Mobject, None] = {}
    self.background_color = config.background_color
    self.background_opacity = config.background_opacity
    """背景的不透明度,从 0 到 1:取配置值,除非另行更改。"""
    self.exposure = 1.0
    """具有[材质][manimgx.Material]的 mobject 所反射的光在显示前被缩放的倍数:
    2 表示亮两倍(一档)。"""
    self.tone_mapping: Literal["linear", "agx"] = "linear"
    """比白色更亮的光如何显示:"linear" 将其截断(被照亮表面的
    颜色原样显示,最高到白色),"agx" 像胶片那样将其柔和过渡(AgX:高光
    向白色去饱和,无任何截断)。被照亮表面射入某像素的光先被平均,
    再一次性显示,如同相机记录的那样。"""
    self.ambient_occlusion = 0.0
    """来自四周的光(环境光或环境光照)被遮挡出角落、折痕以及物体下方
    地面的程度,遮挡来自其附近表面:0 表示完全不遮挡(关闭),1 表示标准
    强度,更大则更暗。只有带[材质][manimgx.Material]的 mobject 才会被该光
    照亮,且只有不透明者才会遮挡或被加暗;太阳光、点光源或聚光灯的光不受
    影响。

    它由视图所显示的内容求得(屏幕空间环境光遮蔽):视图无法看见的
    表面(例如侧着看的墙)不遮挡任何光。光在遮挡它的表面之间反弹,
    因此浅色表面加暗较少,白色表面几乎不加暗。"""
    self.ambient_occlusion_radius = 0.3
    """在某点周围寻找遮挡其光的表面的范围,以场景单位表示:约为
    需加暗的折痕与接触的尺寸。"""
    self.bloom = 0.0
    """具有[材质][manimgx.Material]的 mobject 射入视图的光中,被镜头在其
    周围扩散的部分,作为光晕(bloom),从 0 到 1:0 表示无(关闭),
    0.05 表示镜头的微弱光晕,在比白色更亮的光周围呈纯色,1 表示全部。
    光晕所扩散的正是光本身:视图保留其原有的光,并将其扩散。

    光晕由视图所显示的光扩散而来:更近的物体、同样固定在帧中的 mobject
    会遮挡其后的光。光晕随距光的距离衰减:其一半位于视图高度的二十分之
    一以内,几乎全部位于四分之一以内。它是光,叠加在视图所显示的一切
    之上:在被照亮的表面上,它在色调映射显示二者之前叠加到表面的光上;
    在显示颜料上(背景、无材质的 mobject、固定在帧中的 mobject),色调
    映射将其单独显示并叠加到颜料上,如同屏幕加光(白色保持白色);在
    视图无内容处(透明背景),它单独显示,不透明度等于其亮度。无材质的
    mobject 不发出光:它们永不产生光晕。"""

画框

frame

该视图:场景中的一个矩形,以相机所观察的点为中心,宽与高 等于其所显示的范围。

移动它、缩放它或为其添加动画即可平移与缩放: self.play(self.camera.frame.animate.scale(0.5)) 放大画面,显示一半的 内容,但放大两倍。其轮廓不被绘制(描边宽度为 0)。

frame_center

相机所观察的点:其帧的中心。设置它以将帧移动到该处。

camera.frame_center
源码

manimgx\scene.py

def frame_center(self) -> Point3D:
    """相机所观察的点:其帧的中心。设置它以将帧移动到该处。"""
    return self.frame.get_center()

frame_width

视图的宽度,以场景单位表示:即其帧的宽度。

camera.frame_width
源码

manimgx\scene.py

def frame_width(self) -> float:
    """视图的宽度,以场景单位表示:即其帧的宽度。"""
    return self.frame.width

frame_height

视图的高度,以场景单位表示:即其帧的高度。

camera.frame_height
源码

manimgx\scene.py

def frame_height(self) -> float:
    """视图的高度,以场景单位表示:即其帧的高度。"""
    return self.frame.height

auto_zoom

在二维中将视图适配到一组 mobject 周围:将帧居中于其上, 并调整尺寸以容纳它们。

帧保持其比例:其宽度适配 mobject,或其高度适配 mobject, 取两者中填充较多的一个,并加上 margin。

camera.auto_zoom(mobjects, margin=0, animate=True)
mobjects

要适配的 mobject,帧本身被排除在外;至少须有一个 保留(否则抛出 ValueError)。

margin

视图比 mobject 多出的宽度(或高度),以场景单位表示。

animate

是否返回帧的动画以供播放,而非立即适配。

返回 该动画,以供播放;当 animate 为 False 时,返回适配后的帧。

源码

manimgx\scene.py

def auto_zoom(
    self,
    mobjects: Iterable[Mobject],
    margin: float = 0,
    animate: bool = True,
) -> Animate[Mobject] | Mobject:
    """在二维中将视图适配到一组 mobject 周围:将帧居中于其上,
    并调整尺寸以容纳它们。

    帧保持其比例:其宽度适配 mobject,或其高度适配 mobject,
    取两者中填充较多的一个,并加上 `margin`。

    Args:
        mobjects: 要适配的 mobject,帧本身被排除在外;至少须有一个
            保留(否则抛出 ValueError)。
        margin: 视图比 mobject 多出的宽度(或高度),以场景单位表示。
        animate: 是否返回帧的动画以供播放,而非立即适配。

    Returns:
        该动画,以供播放;当 `animate` 为 False 时,返回适配后的帧。
    """
    mobs = [m for m in mobjects if m is not self.frame]
    if not mobs:
        raise ValueError(
            "Could not determine bounding box of the mobjects given to 'auto_zoom'."
        )
    left, right = (
        min(m.get_critical_point(LEFT)[0] for m in mobs),
        max(m.get_critical_point(RIGHT)[0] for m in mobs),
    )
    up, down = (
        max(m.get_critical_point(UP)[1] for m in mobs),
        min(m.get_critical_point(DOWN)[1] for m in mobs),
    )
    target = (
        (self.frame.animate if animate else self.frame)
        .set_x((left + right) / 2)
        .set_y((up + down) / 2)
    )
    if (right - left) / self.frame.width > (up - down) / self.frame.height:
        return target.set(width=right - left + margin)
    return target.set(height=up - down + margin)

外观

background_color

背景的颜色:取配置值,除非另行更改。可设为任何颜色。

camera.background_color
源码

manimgx\scene.py

def background_color(self) -> ManimColor:
    """背景的颜色:取配置值,除非另行更改。可设为任何颜色。"""
    return self._background_color

background_opacity

背景的不透明度,从 0 到 1:取配置值,除非另行更改。

exposure

具有材质的 mobject 所反射的光在显示前被缩放的倍数: 2 表示亮两倍(一档)。

tone_mapping

比白色更亮的光如何显示:"linear" 将其截断(被照亮表面的 颜色原样显示,最高到白色),"agx" 像胶片那样将其柔和过渡(AgX:高光 向白色去饱和,无任何截断)。被照亮表面射入某像素的光先被平均, 再一次性显示,如同相机记录的那样。

bloom

具有材质的 mobject 射入视图的光中,被镜头在其 周围扩散的部分,作为光晕(bloom),从 0 到 1:0 表示无(关闭), 0.05 表示镜头的微弱光晕,在比白色更亮的光周围呈纯色,1 表示全部。 光晕所扩散的正是光本身:视图保留其原有的光,并将其扩散。

光晕由视图所显示的光扩散而来:更近的物体、同样固定在帧中的 mobject 会遮挡其后的光。光晕随距光的距离衰减:其一半位于视图高度的二十分之 一以内,几乎全部位于四分之一以内。它是光,叠加在视图所显示的一切 之上:在被照亮的表面上,它在色调映射显示二者之前叠加到表面的光上; 在显示颜料上(背景、无材质的 mobject、固定在帧中的 mobject),色调 映射将其单独显示并叠加到颜料上,如同屏幕加光(白色保持白色);在 视图无内容处(透明背景),它单独显示,不透明度等于其亮度。无材质的 mobject 不发出光:它们永不产生光晕。

ambient_occlusion

来自四周的光(环境光或环境光照)被遮挡出角落、折痕以及物体下方 地面的程度,遮挡来自其附近表面:0 表示完全不遮挡(关闭),1 表示标准 强度,更大则更暗。只有带材质的 mobject 才会被该光 照亮,且只有不透明者才会遮挡或被加暗;太阳光、点光源或聚光灯的光不受 影响。

它由视图所显示的内容求得(屏幕空间环境光遮蔽):视图无法看见的 表面(例如侧着看的墙)不遮挡任何光。光在遮挡它的表面之间反弹, 因此浅色表面加暗较少,白色表面几乎不加暗。

ambient_occlusion_radius

在某点周围寻找遮挡其光的表面的范围,以场景单位表示:约为 需加暗的折痕与接触的尺寸。

light_source

光源所在位置:场景中的一个点,为三维着色的 mobject 提供光照 (shade_in_3d)。像任何 mobject 一样移动它。

屏幕上固定

fixed_in_frame_mobjects

固定到屏幕上的 mobject:即传给 add_fixed_in_frame_mobjects 的那些,连同其当前家族的每个成员:之后获得的部分(一个数字的新数位、 更新器或 Transform 产生的部分)也随之固定。

camera.fixed_in_frame_mobjects
源码

manimgx\scene.py

def fixed_in_frame_mobjects(self) -> set[Mobject]:
    """固定到屏幕上的 mobject:即传给
    [`add_fixed_in_frame_mobjects`][manimgx.Camera.add_fixed_in_frame_mobjects]
    的那些,连同其当前家族的每个成员:之后获得的部分(一个数字的新数位、
    更新器或 Transform 产生的部分)也随之固定。"""
    return {m for mob in self._fixed_in_frame for m in mob.get_family()}

add_fixed_in_frame_mobjects

将 mobject 固定到屏幕上:每个按其点所在位置绘制,如同在以原点为中心的 二维视图中,绘制在一切之上。

无论相机的角度、缩放与中心如何,它们都保持在屏幕上的位置:用 to_corner 放置的标题停留在其角落。其中的一切现在及以后都被固定: 之后添加的成员(一个数字的新数位、更新器或 Transform 产生的部分)也 被固定。

camera.add_fixed_in_frame_mobjects(*mobjects)
*mobjects

要固定的 mobject,连同其家族。

源码

manimgx\scene.py

def add_fixed_in_frame_mobjects(self, *mobjects: Mobject) -> None:
    """将 mobject 固定到屏幕上:每个按其点所在位置绘制,如同在以原点为中心的
    二维视图中,绘制在一切之上。

    无论相机的角度、缩放与中心如何,它们都保持在屏幕上的位置:用
    `to_corner` 放置的标题停留在其角落。其中的一切现在及以后都被固定:
    之后添加的成员(一个数字的新数位、更新器或 Transform 产生的部分)也
    被固定。

    Args:
        *mobjects: 要固定的 mobject,连同其家族。
    """
    self._fixed_in_frame.update(dict.fromkeys(mobjects))

其角度,在 3D 中

three_d

相机是否以三维方式观察:带透视与深度,从其轨道观察。

get_phi

相机视线与 z 轴之间的夹角。

camera.get_phi()

返回 该角度,以弧度表示。

源码

manimgx\scene.py

def get_phi(self) -> float:
    """相机视线与 z 轴之间的夹角。

    Returns:
        该角度,以弧度表示。
    """
    return self.phi_tracker.get_value()

get_theta

相机绕 z 轴的角度,从 x 轴逆时针方向计量。

camera.get_theta()

返回 该角度,以弧度表示。

源码

manimgx\scene.py

def get_theta(self) -> float:
    """相机绕 z 轴的角度,从 x 轴逆时针方向计量。

    Returns:
        该角度,以弧度表示。
    """
    return self.theta_tracker.get_value()

get_gamma

相机绕自身视线转动的角度。

camera.get_gamma()

返回 该角度,以弧度表示。

源码

manimgx\scene.py

def get_gamma(self) -> float:
    """相机绕自身视线转动的角度。

    Returns:
        该角度,以弧度表示。
    """
    return self.gamma_tracker.get_value()

get_zoom

相机的放大倍数。

camera.get_zoom()

返回 zoom:1 表示无放大。

源码

manimgx\scene.py

def get_zoom(self) -> float:
    """相机的放大倍数。

    Returns:
        zoom:1 表示无放大。
    """
    return self.zoom_tracker.get_value()

get_focal_distance

相机与其帧中心的距离。

camera.get_focal_distance()

返回 该距离,以场景单位表示。

源码

manimgx\scene.py

def get_focal_distance(self) -> float:
    """相机与其帧中心的距离。

    Returns:
        该距离,以场景单位表示。
    """
    return self.focal_distance_tracker.get_value()

set_phi

立即设置相机视线与 z 轴之间的夹角。

camera.set_phi(value)
value

该角度,以弧度表示:0 表示从正上方俯视 xy 平面。

源码

manimgx\scene.py

def set_phi(self, value: float) -> None:
    """立即设置相机视线与 z 轴之间的夹角。

    Args:
        value: 该角度,以弧度表示:0 表示从正上方俯视 xy 平面。
    """
    self.phi_tracker.set_value(value)

set_theta

立即设置相机绕 z 轴的角度。

camera.set_theta(value)
value

该角度,以弧度表示,从 x 轴逆时针方向计量。

源码

manimgx\scene.py

def set_theta(self, value: float) -> None:
    """立即设置相机绕 z 轴的角度。

    Args:
        value: 该角度,以弧度表示,从 x 轴逆时针方向计量。
    """
    self.theta_tracker.set_value(value)

set_gamma

立即设置相机绕自身视线转动的角度。

camera.set_gamma(value)
value

该角度,以弧度表示。

源码

manimgx\scene.py

def set_gamma(self, value: float) -> None:
    """立即设置相机绕自身视线转动的角度。

    Args:
        value: 该角度,以弧度表示。
    """
    self.gamma_tracker.set_value(value)