用manim实现三维向量和平面的结合

矩阵在线性代数中无处不在。矩阵的列描述了相应的基向量相对于初始基的位置。所有变换后的向量都是变换后的基向量的线性组合它们是矩阵的列,这也被称为线性。对矩阵进行操作的算法本质上只是改变了向量变换的方式,保留了一些性质。

from manim import *  # 导入 Manim 库  

class LinearTransformation3D02(ThreeDScene):  # 定义一个名为 LinearTransformation3D02 的类,继承 ThreeDScene  

    def create_matrix(self, np_matrix):  # 定义一个方法,用于创建矩阵对象  
        m = Matrix(np_matrix)  # 创建一个矩阵 m  
        m.scale(0.5)  # 将矩阵缩放到原来的一半大小  
        m.set_column_colors(GREEN, RED, GOLD)  # 设置矩阵列的颜色  
        m.to_corner(UP + LEFT)  # 将矩阵移动到屏幕的左上角  
        return m  # 返回创建的矩阵对象  

    def construct(self):  # 定义构建场景的方法  
        basis_i_color = GREEN  # 设置 i 向量的颜色为绿色  
        basis_j_color = RED  # 设置 j 向量的颜色为红色  
        basis_k_color = GOLD  # 设置 k 向量的颜色为金色  

        M = np.array([  # 创建一个变换矩阵 M  
            [2, 2, -1],  
            [-2, 1, 2],  
            [3, 1, 0]  
        ])  

        axes = ThreeDAxes()  # 创建三维坐标轴对象  
        axes.set_color(GRAY)  # 设置坐标轴的颜色为灰色  
        axes.add(axes.get_axis_labels())  # 为坐标轴添加轴标签  

        self.set_camera_orientation(phi=75 * DEGREES, theta=-45 * DEGREES)  # 设置相机的旋转角度  

        basis_vector_helper = Tex("$i$" , "," , "$j$" , "," , "$k$")  # 创建基础向量标签  
        basis_vector_helper[0].set_color(basis_i_color)  # 设置 i 向量标签的颜色  
        basis_vector_helper[2].set_color(basis_j_color)  # 设置 j 向量标签的颜色  
        basis_vector_helper[4].set_color(basis_k_color)  # 设置 k 向量标签的颜色  
        basis_vector_helper.to_corner(UP + RIGHT)  # 将标签移动到右上角  

        self.add_fixed_in_frame_mobjects(basis_vector_helper)  # 将基础向量标签固定在屏幕上  
        matrix = self.create_matrix(M)  # 创建矩阵对象  
        self.add_fixed_in_frame_mobjects(matrix)  # 将矩阵固定在屏幕上  

        self.add(axes)  # 添加坐标轴到场景  

        self.begin_ambient_camera_rotation(rate=0.2)  # 开始相机的环绕旋转  

        cube = Cube(side_length=1, fill_color=BLUE, stroke_width=2, fill_opacity=0.1)  # 创建透明的蓝色正方体  
        cube.set_stroke(BLUE_E)  # 设置正方体的边框颜色  

        i_vec = Vector(np.array([1, 0, 0]), color=basis_i_color)  # 创建 i 向量  
        j_vec = Vector(np.array([0, 1, 0]), color=basis_j_color)  # 创建 j 向量  
        k_vec = Vector(np.array([0, 0, 1]), color=basis_k_color)  # 创建 k 向量  

        i_vec_new = Vector(M @ np.array([1, 0, 0]), color=basis_i_color)  # 计算变换后的 i 向量  
        j_vec_new = Vector(M @ np.array([0, 1, 0]), color=basis_j_color)  # 计算变换后的 j 向量  
        k_vec_new = Vector(M @ np.array([0, 0, 1]), color=basis_k_color)  # 计算变换后的 k 向量  

        self.play(  # 播放初始动画  
            Create(cube),  # 创建正方体动画  
            GrowArrow(i_vec),  # 显示 i 向量动画  
            GrowArrow(j_vec),  # 显示 j 向量动画  
            GrowArrow(k_vec),  # 显示 k 向量动画  
            Write(basis_vector_helper)  # 显示基础向量标签动画  
        )  

        self.wait(2)  # 等待 2 秒以展示初始状态  

        # 变换 i 向量  
        matrix_anim_i = ApplyMatrix(M, cube)  # 创建应用矩阵 M 的动画  
        self.play(  
            matrix_anim_i,  # 应用矩阵的动画  
            Transform(i_vec, i_vec_new)  # 变换 i 向量到新的位置  
        )  
        self.wait(2)  # 等待 2 秒  

        # 变换 j 向量  
        matrix_anim_j = ApplyMatrix(M, cube)  # 创建应用矩阵 M 的动画  
        self.play(  
            matrix_anim_j,  # 应用矩阵的动画  
            Transform(j_vec, j_vec_new)  # 变换 j 向量到新的位置  
        )  
        self.wait(2)  # 等待 2 秒  

        # 变换 k 向量  
        matrix_anim_k = ApplyMatrix(M, cube)  # 创建应用矩阵 M 的动画  
        self.play(  
            matrix_anim_k,  # 应用矩阵的动画  
            Transform(k_vec, k_vec_new)  #
                        Transform(k_vec, k_vec_new)  # 变换 k 向量到新的位置  
        )  
        self.wait(2)  # 等待 2 秒  

        # 将正方体移动到新的 i、j 和 k 向量方向对应的位置  
        new_position = (i_vec_new.get_end() + j_vec_new.get_end() + k_vec_new.get_end()) / 3  # 计算正方体的新位置  
        self.play(cube.animate.move_to(new_position))  # 移动正方体到新位置  

        # 最后等待几秒钟以展示最终效果  
        self.wait(7)  # 等待 7 秒以展示最终状态

 

 

 

 

 

代码解释

  1. 向量变换:

    • 在每次动画中,ApplyMatrix(M, cube) 将变换矩阵 M 应用于立方体 cube,展示了基于矩阵变换后的三维形状变化。
    • Transform(i_vec, i_vec_new) 类似地将旧的向量变换为新的向量。这个过程重复了三次,分别对应了 ij 和 k 向量的变化。
  2. 新位置计算:

    • 在所有的向量都完成变换后,代码计算了正方体的新位置,这个新位置是所有三维向量的终点的平均值。这意味着正方体会中心对齐到建立的新基座。
  3. 节奏控制:

    • self.wait(2) 和 self.wait(7) 用于控制每个动画阶段之间的间隔以及展示效果。这样使得观众有足够的时间来理解每个变换过程。

示例代码2:

在animm中,有一个特殊的ApplyMatrix动画,允许我们原生地将矩阵应用到对象的每个3D顶点。

from manimlib.imports import *

class LinearTransformation3D(ThreeDScene):

    CONFIG = {
        "x_axis_label": "$x$",
        "y_axis_label": "$y$",
        "basis_i_color": GREEN,
        "basis_j_color": RED,
        "basis_k_color": GOLD
    }

    def create_matrix(self, np_matrix):

        m = Matrix(np_matrix)

        m.scale(0.5)
        m.set_column_colors(self.basis_i_color, self.basis_j_color, self.basis_k_color)

        m.to_corner(UP + LEFT)

        return m

    def construct(self):

        M = np.array([
            [2, 2, -1],
            [-2, 1, 2],
            [3, 1, -0]
        ])

        axes = ThreeDAxes()
        axes.set_color(GRAY)
        axes.add(axes.get_axis_labels())

        self.set_camera_orientation(phi=75 * DEGREES, theta=-45 * DEGREES)

        # basis vectors i,j,k
        basis_vector_helper = TextMobject("$i$", ",", "$j$", ",", "$k$")
        basis_vector_helper[0].set_color(self.basis_i_color)
        basis_vector_helper[2].set_color(self.basis_j_color)
        basis_vector_helper[4].set_color(self.basis_k_color)

        basis_vector_helper.to_corner(UP + RIGHT)

        self.add_fixed_in_frame_mobjects(basis_vector_helper)

        # matrix
        matrix = self.create_matrix(M)

        self.add_fixed_in_frame_mobjects(matrix)

        # axes & camera
        self.add(axes)

        self.begin_ambient_camera_rotation(rate=0.2)

        cube = Cube(side_length=1, fill_color=BLUE, stroke_width=2, fill_opacity=0.1)
        cube.set_stroke(BLUE_E)

        i_vec = Vector(np.array([1, 0, 0]), color=self.basis_i_color)
        j_vec = Vector(np.array([0, 1, 0]), color=self.basis_j_color)
        k_vec = Vector(np.array([0, 0, 1]), color=self.basis_k_color)

        i_vec_new = Vector(M @ np.array([1, 0, 0]), color=self.basis_i_color)
        j_vec_new = Vector(M @ np.array([0, 1, 0]), color=self.basis_j_color)
        k_vec_new = Vector(M @ np.array([0, 0, 1]), color=self.basis_k_color)

        self.play(
            ShowCreation(cube),
            GrowArrow(i_vec),
            GrowArrow(j_vec),
            GrowArrow(k_vec),
            Write(basis_vector_helper)
        )

        self.wait()

        matrix_anim = ApplyMatrix(M, cube)

        self.play(
            matrix_anim,
            Transform(i_vec, i_vec_new, rate_func=matrix_anim.get_rate_func(),
                      run_time=matrix_anim.get_run_time()),
            Transform(j_vec, j_vec_new, rate_func=matrix_anim.get_rate_func(),
                      run_time=matrix_anim.get_run_time()),
            Transform(k_vec, k_vec_new, rate_func=matrix_anim.get_rate_func(),
                      run_time=matrix_anim.get_run_time())
        )

        self.wait()

        self.wait(7)

示例代码3: 

在上一篇文章中,我们实现了一个高级版本的高斯消去算法(PA = LU分解)。我们可以用下面的方法类似地实现纯高斯消去算法:

def gauss(a):
    m = a.shape[0]

    for x in range(m):
        pivotRow = x

        # search for the best pivot
        for y in range(x + 1, m, 1):
            if abs(a[y][x]) > abs(a[pivotRow][x]):
                pivotRow = y

        if a[pivotRow][x] == 0:
            # we didn't find any row with a non-zero leading coefficient
            # that means that the matrix has all zeroes in this column
            # so we don't need to search for pivots after all for the current column x
            continue

        # did we just use a pivot that is not on the diagonal?
        if pivotRow != x:
            # swap the pivot row with the current row in both A and L matrices
            a[[x, pivotRow]] = a[[pivotRow, x]]
            yield a

        # now the pivot row is x
        # search for rows where the leading coefficient must be eliminated
        for y in range(x + 1, m, 1):
            currentValue = a[y][x]
            if currentValue == 0:
                # variable already eliminated, nothing to do
                continue

            pivot = a[x][x]
            assert pivot != 0  # just in case, we already made sure the pivot is not zero

            pivotFactor = currentValue / pivot

            # subtract the pivot row from the current row

            a[y][x] = 0

            for i in range(x + 1, m, 1):
                a[y][i] -= pivotFactor * a[x][i]

            yield a

 注意,在我们想要可视化的每个矩阵运算之后,我都添加了一个yield语句。Python中的生成器非常适合可视化算法,因为它们允许我们保存当前状态,生成值,然后在调用者处理接收到的值时继续执行。在这种情况下,我们在算法运行时生成矩阵的每个版本。
现在我们可以调整和扩展用于呈现线性变换的代码,以便在高斯消去的每一步都这样做:

class Gauss3D(ThreeDScene):

    CONFIG = {
        "x_axis_label": "$x$",
        "y_axis_label": "$y$",
        "basis_i_color": GREEN,
        "basis_j_color": RED,
        "basis_k_color": GOLD
    }

    def create_matrix(self, np_matrix):

        m = Matrix(np_matrix)

        m.scale(0.5)
        m.set_column_colors(self.basis_i_color, self.basis_j_color, self.basis_k_color)

        m.to_corner(UP + LEFT)

        return m

    def construct(self):

        M = np.array([
            [-1.0, 1.0, -2.0],
            [-4.0, -2.0, 1.0],
            [-2.0, 2.0, 3.0]
        ])

        # axes
        axes = ThreeDAxes()
        axes.set_color(GRAY)
        axes.add(axes.get_axis_labels())

        self.set_camera_orientation(phi=55 * DEGREES, theta=-45 * DEGREES)

        # basis vectors i,j,k
        basis_vector_helper = TextMobject("$i$", ",", "$j$", ",", "$k$")
        basis_vector_helper[0].set_color(self.basis_i_color)
        basis_vector_helper[2].set_color(self.basis_j_color)
        basis_vector_helper[4].set_color(self.basis_k_color)

        basis_vector_helper.to_corner(UP + RIGHT)

        self.add_fixed_in_frame_mobjects(basis_vector_helper)

        # matrix
        matrix = self.create_matrix(M)

        self.add_fixed_in_frame_mobjects(matrix)

        # axes & camera
        self.add(axes)

        self.begin_ambient_camera_rotation(rate=0.15)

        cube = Cube(side_length=1, fill_color=BLUE, stroke_width=2, fill_opacity=0.1)
        cube.set_stroke(BLUE_E)  # cube.set_stroke(TEAL_E)

        i_vec = Vector(np.array([1, 0, 0]), color=self.basis_i_color)
        j_vec = Vector(np.array([0, 1, 0]), color=self.basis_j_color)
        k_vec = Vector(np.array([0, 0, 1]), color=self.basis_k_color)

        i_vec_new = Vector(M @ np.array([1, 0, 0]), color=self.basis_i_color)
        j_vec_new = Vector(M @ np.array([0, 1, 0]), color=self.basis_j_color)
        k_vec_new = Vector(M @ np.array([0, 0, 1]), color=self.basis_k_color)

        self.play(
            ShowCreation(cube),
            GrowArrow(i_vec),
            GrowArrow(j_vec),
            GrowArrow(k_vec),
            Write(basis_vector_helper)
        )

        self.wait()

        matrix_anim = ApplyMatrix(M, cube)

        self.play(
            matrix_anim,
            ReplacementTransform(i_vec, i_vec_new, rate_func=matrix_anim.get_rate_func(),
                                 run_time=matrix_anim.get_run_time()),
            ReplacementTransform(j_vec, j_vec_new, rate_func=matrix_anim.get_rate_func(),
                                 run_time=matrix_anim.get_run_time()),
            ReplacementTransform(k_vec, k_vec_new, rate_func=matrix_anim.get_rate_func(),
                                 run_time=matrix_anim.get_run_time())
        )

        self.wait()

        i_vec, j_vec, k_vec = i_vec_new, j_vec_new, k_vec_new

        self.wait(2)

        for a in gauss(M):

            a_rounded = np.round(a.copy(), 2)

            self.remove(matrix)

            matrix = self.create_matrix(a_rounded)

            self.add_fixed_in_frame_mobjects(matrix)

            # transformed cube
            new_cube = Cube(side_length=1, fill_color=BLUE, stroke_width=2, fill_opacity=0.1)
            new_cube.set_stroke(BLUE_E)

            new_cube.apply_matrix(a)

            # vectors
            i_vec_new = Vector(a @ np.array([1, 0, 0]), color=self.basis_i_color)
            j_vec_new = Vector(a @ np.array([0, 1, 0]), color=self.basis_j_color)
            k_vec_new = Vector(a @ np.array([0, 0, 1]), color=self.basis_k_color)

            # prepare and run animation
            cube_anim = ReplacementTransform(cube, new_cube)

            self.play(
                cube_anim,
                ReplacementTransform(i_vec, i_vec_new, rate_func=cube_anim.get_rate_func(),
                                     run_time=cube_anim.get_run_time()),
                ReplacementTransform(j_vec, j_vec_new, rate_func=cube_anim.get_rate_func(),
                                     run_time=cube_anim.get_run_time()),
                ReplacementTransform(k_vec, k_vec_new, rate_func=cube_anim.get_rate_func(),
                                     run_time=cube_anim.get_run_time())
            )

            self.wait()

            cube = new_cube
            i_vec, j_vec, k_vec = i_vec_new, j_vec_new, k_vec_new

            self.wait(1)

        self.wait(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yasen.M

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值