manim 4.2

from manim import *
import sympy as sym
import numpy as np
if type("xxp's manim") == str:
    # 不写在下方 construct 函数中,因为这可能使用到其内部的函数,我想与其分离,因为如果 construct 内部的函数改写了,我此处的类也需要改写
    if type('remarks') == str:
        pass
        # 代码主要由三部分构成:1、自定义类;2、manim 类;3、执行代码部分
        # 其中代码执行部分又由三部分构成:1、变量定义区(包括物件和动画);2、变量初始化区;3、动画区
    if type('function') == str:
        def harmonic_func(u, v, l, m):
            # 请不要使用 if l == x & m == y 这种形式,会报错
            if l == 0 & m == 0:
                r = np.sqrt(1/(4*PI))
            elif l == 1:
                if m == 0:
                    r = np.sqrt(3/(4*PI))*np.cos(u)
                elif m == 1:
                    r = np.sqrt(3/(4*PI))*np.sin(u)*np.cos(v)
                elif m == -1:
                    r = np.sqrt(3/(4*PI))*np.sin(u)*np.sin(v)
            elif l == 2:
                if m == 0:
                    r = np.sqrt(5/(16*PI))*(3*np.cos(u)**2 - 1)
                if m == 1:
                    r = np.sqrt(15/(4*PI))*np.sin(u)*np.cos(u)*np.cos(v)
                if m == -1:
                    r = np.sqrt(15/(4*PI))*np.sin(u)*np.cos(u)*np.sin(v)
                if m == 2:
                    r = np.sqrt(15/(16*PI))*np.sin(u)**2*np.sin(2*v)
                if m == -2:
                    r = np.sqrt(15/(16*PI))*np.sin(u)**2*np.cos(2*v)
            elif l == 3:
                if m == 0:
                    r = 1/4*np.sqrt(7/PI)*(5*np.cos(u)**3 - 3*np.cos(u))
                if m == 1:
                    r = 1/8*np.sqrt(42/PI)*np.sin(u)*(5*np.cos(u)**2 - 1)*np.cos(v)
                if m == -1:
                    r = 1/8*np.sqrt(42/PI)*np.sin(u)*(5*np.cos(u)**2 - 1)*np.sin(v)
                if m == 2:
                    r = 1/4*np.sqrt(105/PI)*np.sin(u)**2*np.cos(u)*np.sin(2*v)
                if m == -2:
                    r = 1/4*np.sqrt(105/PI)*np.sin(u)**2*np.cos(u)*np.cos(2*v)
                if m == 3:
                    r = 1/8*np.sqrt(70/PI)*np.sin(u)**3*np.cos(3*v)
                if m == -3:
                    r = 1/8*np.sqrt(70/PI)*np.sin(u)**3*np.sin(3*v)
            else:
                raise Exception('l or m is too large')
            return r
    if type('mobject') == str:
        class Atom(VGroup):
            def __init__(
                self,
                quantum_num_li: list,  # each item in this list is a list
                position_correction: bool = False,  # 这个是专门为 sp2 杂化设计的,因为我发现 sp2 杂化时原子核不在中心,这是因为正三角形放置在画布中时不会以自身的中心为中心点,而是会产生一些偏移,于是我们使用 p 轨道定位
                show_nucleus: bool = True,
                **kwargs
            ):
                super().__init__(**kwargs)  # 如果不使用这句话,那么 self.add 会失效
                nucleus = Sphere(fill_opacity=0.7, stroke_width=0, radius=0.3).set_color(
                    "#9AFF9A")
                axes = ThreeDAxes(x_range=(-5, 5, 1), y_range=(-5, 5, 1), z_range=(-5, 5, 1),
                                  x_length=5, y_length=5, z_length=5)  # 注意默认的 x, y, z 轴长度不相等,图像会拉伸
                orbits = VGroup()
                for num in quantum_num_li:
                    orbit = Surface(
                        # 在不杂化的原子中,num 是含有两个元素的列表,分布代表 l 和 m;在杂化的原子中,num 是含有大于等于六个元素的列表,一部分包括杂化轨道的系数
                        lambda u, v: axes.c2p(*self.specific_harmonic_func(u, v, num)),
                        resolution=(20, 20),
                        v_range=[0, 2*PI],
                        u_range=[0, 2*PI],
                        checkerboard_colors=[BLUE],
                        fill_opacity=0.3,
                        stroke_width=0,
                    )
                    orbits.add(orbit)
                nucleus_radius = nucleus.width
                # if orbits.depth > orbits.width:  # 事实上,三维物体要考虑 width, height, depth
                #     orbits.set(depth=6*nucleus_radius)
                # else:
                #     orbits.set(width=6*nucleus_radius)

                if position_correction:
                    distance_deviation = nucleus.get_center() - orbits[-1].get_center()
                    orbits.shift(distance_deviation)
                    orbits.rotate(angle = PI/2, axis = [1,0,0])  # sp2 杂化默认视角在 p 轨道上方,旋转 PI/2 从侧面观察
                    nucleus.rotate(angle = PI/2, axis = [1,0,0])  # 不能使用 self.rotate(),因为 self.add() 在下方才定义

                self.nucleus = nucleus
                self.orbits = orbits
                if show_nucleus:
                    self.add(nucleus, orbits)
                else:
                    self.add(orbits)

            def specific_harmonic_func(self, u, v, num) -> list:  # 这个函数根据是否杂化而变化
                pass
        class AutographIntroduction(Group):
            def __init__(
                self,
                image: str,
                name: str,
                date_of_birth_and_death: str,
                **kwargs
            ):
                super().__init__(**kwargs)
                image = ImageMobject(image)
                if image.width > image.height:
                    image.set(width = 5.5)
                else:
                    image.set(height = 5.5)
                image.shift(3.5*LEFT+0.8*UP)
                name = Narrator(name).next_to(image,DOWN)
                date_of_birth_and_death = Narrator(date_of_birth_and_death).scale(0.8).next_to(name,DOWN)
                self.add(image, name, date_of_birth_and_death)
        class ColorBar(VGroup):
            def __init__(
                self,
                start_color: color = BLUE,
                end_color: color = RED,
                **kwargs
            ):
                super().__init__(**kwargs)
                width = 7
                height = 0.5
                bar = RoundedRectangle(width=width, height=height, fill_opacity=1, corner_radius=0.2).set_color_by_gradient([end_color, start_color])
                bar.set(stroke_color=WHITE)
                pointer = Triangle(radius=0.2, fill_opacity=1).rotate(PI/3)
                self.start_color = start_color
                self.end_color = end_color
                self.width = width
                self.height = height
                self.bar = bar
                self.pointer = pointer
                self.add(bar, pointer)
                self.arrange(direction=np.array([0,1,0]),buff=0.1)  # 如果使用 [0,1,0] 会报错

            def begin_color_update(self):
                def update_color(pointer):
                    position_deviation = (pointer.get_x() - self.bar.get_x() + self.width/2)/self.width  # 范围为 0 到 1,但是会有一点点的偏差,可能超过 1
                    if position_deviation > 1:
                        position_deviation = 1
                    pointer.set_color(interpolate_color(self.start_color, self.end_color, position_deviation))
                self.pointer.add_updater(update_color)
            def end_color_update(self):
                self.pointer.clear_updater()
            def pointer_to_rightmost_end(self):  # 效果并不理想
                distance = self.bar.get_x() + self.width/2 - self.pointer.get_x()
                return ApplyMethod(self.pointer.shift, distance * RIGHT)
            def pointer_to_leftmost_end(self):  # 效果并不理想
                distance = self.bar.get_x() - self.width/2 - self.pointer.get_x()
                return ApplyMethod(self.pointer.shift, distance * RIGHT)
        class DotGroup(VGroup):
            def __init__(
                self,
                num: int = 10,
                internal_radius: float = 0,
                external_radius: float = 2.5,
                scale_min: float = 0.6,
                scale_max: float = 1.4,
                **kwargs
            ):
                super().__init__(**kwargs)
                for i in range(num):
                    radius = internal_radius + (external_radius - internal_radius)*np.random.rand()
                    scale = scale_min + (scale_max - scale_min)*np.random.rand()
                    theta = np.random.rand()*2*PI
                    self.add(Dot().copy().shift(radius*np.cos(theta)*RIGHT+radius*np.sin(theta)*UP).scale(scale))
        class HybridAtom(Atom):
            def __init__(
                self,
                hybrid_type: int,
                **kwargs
            ):
                self.hybrid_type = hybrid_type
                quantum_num_li = self.hybrid_coefficient(hybrid_type)
                if hybrid_type == 2:
                    position_correction = True
                else:
                    position_correction = False
                super().__init__(quantum_num_li, position_correction=position_correction, **kwargs)

            def hybrid_coefficient(self, hybrid_type):
                if hybrid_type == 1:
                    coe = [[[1/np.sqrt(2), 0, 0], [1/np.sqrt(2), 1, 1]], [[1/np.sqrt(2), 0, 0], [-1/np.sqrt(2), 1, 1]], [
                        [1, 1, -1]], [[1, 1, 0]]]  # this represents f1 = s + px, f2 = x - px, f3 = p, f4 = p
                elif hybrid_type == 2:
                    coe = [[[np.sqrt(1/3), 0, 0], [np.sqrt(2/3), 1, 1]], [[np.sqrt(1/3), 0, 0], [-np.sqrt(1/6), 1, 1], [
                        np.sqrt(1/2), 1, -1]], [[np.sqrt(1/3), 0, 0], [-np.sqrt(1/6), 1, 1], [-np.sqrt(1/2), 1, -1]], [[1, 1, 0]]]
                elif hybrid_type == 3:
                    coe = [[[1, 0, 0], [1, 1, 1], [1, 1, -1], [1, 1, 0]], [[1, 0, 0], [1, 1, 1], [-1, 1, -1], [-1, 1, 0]],
                           [[1, 0, 0], [-1, 1, 1], [1, 1, -1], [-1, 1, 0]], [[1, 0, 0], [-1, 1, 1], [-1, 1, -1], [1, 1, 0]]]
                return coe

            def specific_harmonic_func(self, u, v, num) -> list:
                r = 0
                for i in num:
                    r += i[0]*harmonic_func(u, v, i[1], i[2])
                r = r ** 2
                return [r*np.sin(u)*np.cos(v), r*np.sin(u)*np.sin(v), r*np.cos(u)]
            
            def correct_position(self, direction):  # 将杂化的轨道调整到 direction 的方向
                # 这个函数的作用效果取决于是否使用了 set_camera_orientation(),例如,我们现在想要让杂化轨道朝右,如果没有使用此方法,direction 为 [1,0,0],如果使用了此方法,direction 为 [0,1,0],这是因为 set_camera_orientation() 会使x, y 轴互换
                if self.hybrid_type == 1:
                    orbits_direction = [1,0,0]
                elif self.hybrid_type == 2:
                    orbits_direction = [1,0,0]
                elif self.hybrid_type == 3:
                    orbits_direction = [1,1,1]
                angle = angle_between_vectors(orbits_direction, direction)
                axis = get_unit_normal(orbits_direction, direction)
                # 旋转之后仍然需要调整角度来和坐标轴平行
                # sp3: 15*DEGREES
                self.rotate(angle = angle, axis = axis)

                return self
        class LinesBetweenCenterAndVertexesOfPolyhedron(VGroup):
            def __init__(
                self,
                mobject,
                **kwargs
            ):
                # 只适用于多面体
                super().__init__(**kwargs)
                vertexes = mobject.vertex_coords
                for vertex in vertexes:
                    line = DashedLine(mobject.get_center(), vertex, shade_in_3d = True)
                    self.add(line)
        class MusicRecommender(Group):
            def __init__(
                self,
                recommender: str,
                music: str,
                **kwargs
            ):
                super().__init__(**kwargs)
                image = ImageMobject(f"D:\\manimIMG\\{recommender}.png").set(width=1)
                music = Text(music, font='STFangSong').scale(0.7).next_to(img,10*LEFT)
                name = ImageMobject(f"D:\\manimIMG\\{recommender}name.png").match_height(music).next_to(img,2*RIGHT)
                self.add(image, music, name)
        class Narrator(Text):
            def __init__(
                self,
                text: str,
                font_type: int = 1,
                **kwargs
            ):
                if font_type == 1:
                    font = "STZhongsong"
                elif font_type == 2:
                    font = "STFangsong"
                super().__init__(text, font=font, **kwargs)
                self.to_edge(DOWN).scale(0.6)
        class TextFlower(VGroup):
            def __init__(
                self,
                text: str,
                color_type: int = 1,
                use_latex: bool = False,
                font: str = 'STZhongsong',
                **kwargs
            ):
                super().__init__(**kwargs)
                if color_type == 1:
                    color = ['#f3e9e0', '#fea8a9']
                elif color_type == 2:
                    color = ['#f3e9e0', '#b1d85c']
                elif color_type == 3:
                    color = ['#f3e9e0', '#6A5ACD']

                if use_latex:
                    text = MathTex(text, stroke_width=2).set_color(color = color)
                else:
                    text = Text(text, font = font, stroke_width=2).set_color(color = color)
                flower = SVGMobject(r"D:\manimSVG\figures\icon.svg")[8].rotate(PI/2).set_color(color = color[::-1]).scale(1.5)
                text.next_to(flower, RIGHT)
                self.add(flower, text)
                self.set(height = 0.6)
                self.move_to(ORIGIN)
        class UnhybridAtom(Atom):
            def __init__(
                self,
                *args,  # this list contains l, m
                **kwargs
            ):
                super().__init__([[*args]], **kwargs)

            def specific_harmonic_func(self, u, v, num) -> list:
                r = harmonic_func(u, v, *num)
                r = r ** 2
                return [r*np.sin(u)*np.cos(v), r*np.sin(u)*np.sin(v), r*np.cos(u)]

        class xa(Atom):  # Atom
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class xai(AutographIntroduction):  # AutographIntroduction
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class xcb(ColorBar):  # ColorBar
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class xdg(DotGroup):  # DotGroup
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class xha(HybridAtom):  # HybridAtom
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class xlb(LinesBetweenCenterAndVertexesOfPolyhedron):  # LinesBetweenCenterAndVertexesOfPolyhedron
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class xmr(MusicRecommender):  # MusicRecommender
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class xn(Narrator):  # Narrator
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class xtf(TextFlower):  # TextFlower
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class xua(UnhybridAtom):  # UnhybridAtom
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
    if type('animation') == str:
        class CorrespondingPartsTransform(AnimationGroup):
            def __init__(
                self,
                names,
                *args,
                **kwargs
            ):
                # names is a list of mobjects
                # args represent lists, such as [0,1,[0,0,1,1,2,2]], the 1st and 2nd numbers represent the index of mobject in the names list
                def pretrans0(name, i):  # get the transforming part
                    if type(i) == int:
                        back = name[i]
                    elif type(i) == str:
                        m = i.find('.')
                        n1 = int(i[0:m])
                        n2 = int(i[m+1:])
                        back = name[n1:n2]
                    return back

                def pretrans(name, i):
                    if type(i) == int:
                        back = name[i]
                    elif type(i) == str:
                        m = i.find('.')
                        n1 = int(i[0:m])
                        n2 = int(i[m+1:])
                        back = name[n1:n2]
                    elif type(i) == list:
                        back = VGroup()
                        for j in i:
                            item = pretrans0(name, j)
                            back.add(item)
                    return back

                ag = []
                itemli = []
                for l in names:
                    itemli.append(list(range(len(l))))
                # delete the tranformed part
                def countli0(name, i):
                    if type(i) == int:
                        name.remove(i)
                    elif type(i) == str:
                        m = i.find('.')
                        n1 = int(i[0:m])
                        n2 = int(i[m+1:])-1
                        p1 = name.index(n1)
                        p2 = name.index(n2)
                        del name[p1:p2+1]

                def countli(name, i):
                    if type(i) == int:
                        name.remove(i)
                    elif type(i) == str:
                        m = i.find('.')
                        n1 = int(i[0:m])
                        n2 = int(i[m+1:])-1
                        p1 = name.index(n1)
                        p2 = name.index(n2)
                        del name[p1:p2+1]
                    elif type(i) == list:
                        for j in i:
                            item = countli0(name, j)

                set1 = set()
                set2 = set()
                # two groups of transforming
                for m in range(len(args)):
                    n = 0
                    for i in args[m][2]:
                        n += 1
                        if n % 2 != 0:
                            u = pretrans(names[args[m][0]], i)
                            countli(itemli[args[m][0]], i)
                            set1.add(args[m][0])
                        else:
                            v = pretrans(names[args[m][1]], i)
                            item = Transform(u,v)  # 如果使用改进的 at(),会显示未定义
                            ag.append(item)
                            countli(itemli[args[m][1]], i)
                            set2.add(args[m][1])

                for i in set1:
                    for ii in itemli[i]:
                        names[i][ii].save_state()
                        ag.append(ShrinkToCenter(names[i][ii]))
                for j in set2:
                    for jj in itemli[j]:
                        ag.append(GrowFromCenter(names[j][jj]))

                super().__init__(*ag, **kwargs)
        class CorrespondingPartsTransformOrigin(AnimationGroup):
            def __init__(
                self,
                original_mobject: mobject,
                target_mobject: mobject,
                corresponding_parts_list: list,
                **kwargs
            ):
                # a and b represent mobjects, c represents the corresponding transform parts
                def pretrans0(name, i):
                    if type(i) == int:
                        back = name[i]
                    elif type(i) == str:
                        m = i.find('.')
                        n1 = int(i[0:m])
                        n2 = int(i[m+1:])
                        back = name[n1:n2]
                    return back

                def pretrans(name, i):
                    if type(i) == int:
                        back = name[i]
                    elif type(i) == str:
                        m = i.find('.')
                        n1 = int(i[0:m])
                        n2 = int(i[m+1:])
                        back = name[n1:n2]
                    elif type(i) == list:
                        back = VGroup()
                        for j in i:
                            item = pretrans0(name, j)
                            back.add(item)
                    return back

                n = 0
                ag = []
                for i in corresponding_parts_list:
                    n += 1
                    if n % 2 != 0:
                        u = pretrans(original_mobject, i)
                    else:
                        v = pretrans(target_mobject, i)
                        item = Transform(u,v)
                        ag.append(item)
                super().__init__(*ag, **kwargs)
        class DotFadeIn(AnimationGroup):
            def __init__(
                self,
                *mobjects,
                **kwargs
            ):
                ag = []
                for i in mobjects:
                    dots = DotGroup(len(i)).set_color(WHITE).set_opacity(0)
                    ag.append(Transform(dots,i))
                super().__init__(*ag, **kwargs)
        class DotFadeOut(AnimationGroup):
            def __init__(
                self,
                *mobjects,
                **kwargs
            ):
                ag = []
                for i in mobjects:
                    dots = DotGroup(len(i)).set_color(WHITE).set_opacity(0)
                    ag.append(Transform(i,dots))
                super().__init__(*ag, **kwargs)
        class FadeInDownward(AnimationGroup):
            def __init__(
                self,
                *mobjects,
                **kwargs
            ):
                ag = []
                for mobject in mobjects:
                    ag.append(FadeIn(mobject, shift=DOWN))
                super().__init__(*ag, **kwargs)
        class FadeOutDownward(AnimationGroup):
            def __init__(
                self,
                *mobjects,
                **kwargs
            ):
                ag = []
                for mobject in mobjects:
                    ag.append(FadeOut(mobject, shift=DOWN))
                super().__init__(*ag, **kwargs)
        class FisheyeGrow(AnimationGroup):
            def __init__(
                self,
                *mobjects,
                **kwargs
            ):
                # 注意如果对 Text 使用此动画,要使用索引才能生效:Text()[0]
                ag = []
                for a in mobjects:
                    a0 = a.copy()
                    myl = 5  # myl represents the fish eye's border, default: 5
                    li = a.points
                    def getlen(k):
                        return np.sqrt(k[0]**2 + k[1]**2 + k[2]**2)
                    for i in li:
                        times = 0.04*(myl - getlen(i))**3
                        i *= times  # the further, times is smaller
                    ag.append(Transform(a0,a))
                super().__init__(*ag, **kwargs)
        class FisheyeShrink(AnimationGroup):
            def __init__(
                self,
                *mobjects,
                **kwargs
            ):
                # 注意如果对 Text 使用此动画,要使用索引才能生效:Text()[0]
                ag = []
                for a in mobjects:
                        a0 = a.copy()
                        li = a.points
                        def getlen(k):
                            return np.sqrt(k[0]**2 + k[1]**2 + k[2]**2)
                        for i in li:
                            times = 0.02*getlen(i)**3 + 0.5  # 0.5 represent the minimum, because when the point is near center, genlen() return a number near 0
                            i *= times  # the further, times is larger
                        ag.append(Transform(a0,a))
                super().__init__(*ag, **kwargs)
        class RotateAboutOwnAxis(Rotate):
            def __init__(
                self,
                mobject,
                *args,
                **kwargs
            ):
                super().__init__(mobject, axis = [0,np.sqrt(3),1], *args, **kwargs)
        class SubTextFlowersCatalogue(AnimationGroup):
            def __init__(
                self,
                **kwargs
            ):
                super().__init__(**kwargs)
            
            def begin_animation(self, text):
                text_flower = TextFlower(text, color_type=2, font="Cambria Math").to_corner(UL).shift(0.8*DOWN)
                animation = DrawBorderThenFill(text_flower)
                self.text_flower = text_flower
                super().__init__(animation)
                return self
            
            def alter(self, text):
                text_flower_after = TextFlower(text, color_type=2, font="Cambria Math").to_corner(UL).shift(0.8*DOWN)
                text_flower = self.text_flower
                animation1 = yfod(text_flower)
                animation2 = yfid(text_flower_after)
                self.text_flower = text_flower_after
                super().__init__(animation1, animation2)
                return self
            
            def end_animation(self):
                text_flower = self.text_flower
                animation = FadeOut(text_flower)
                super().__init__(animation)
                return self
        class TextFlowersCatalogue(Succession):
            def __init__(
                self,
                texts: list,  # a list of texts
                grid: list,
                buff: float = 2, # the spacing of each item
                **kwargs
            ):
                text_flowers = VGroup(*map(TextFlower, texts)).arrange_in_grid(*grid,buff=buff)
                self.text_flowers = text_flowers
                super().__init__(**kwargs)
            def begin_animation(self):  # 注意不能使用 begin 命名
                text_flowers = self.text_flowers
                flower_num = len(text_flowers)
                flower = TextFlower("").shift(0.7*LEFT)
                flowers = VGroup(*[flower.copy() for i in range(flower_num)])  # 注意不能直接写成 VGroup(... for i in ...),必须使用 *
                animation1 = DrawBorderThenFill(flowers)
                animation2 = AnimationGroup(*[Rotate(flowers[i+1], angle=2*PI/flower_num*(i+1), about_point=ORIGIN) for i in range(flower_num-1)], lag_ratio=0.3)
                animation3 = Transform(flowers, text_flowers)  # 我用 ReplacementTransform 不能实现预期的效果
                self.text_flowers = flowers  # 注意,这里改变成了 transform 的对象 flowers
                
                super().__init__(animation1, animation2, animation3)
                return self
            
            def forward(self, index):
                text_flowers = self.text_flowers
                text_flowers_after = text_flowers[index-1].copy().to_corner(UL)
                shift_distance = text_flowers_after.get_center() - text_flowers[index-1].get_center()
                animation1 = ApplyMethod(text_flowers[index-1].shift, shift_distance)
                animation2 = FadeOut(text_flowers[0:index-1])
                animation3 = FadeOut(text_flowers[index:])
                self.shift_distance = shift_distance
                self.index = index

                return AnimationGroup(animation1, animation2, animation3)

            def remaining_text_flower(self, index):
                text_flowers = self.text_flowers
                text_flowers_after = text_flowers[index-1].copy().to_corner(UL)
                shift_distance = text_flowers_after.get_center() - text_flowers[index-1].get_center()
                self.shift_distance = shift_distance
                self.index = index

                return text_flowers[index-1].to_corner(UL)

            def backward(self):
                text_flowers = self.text_flowers
                index = self.index
                shift_distance = self.shift_distance
                animation1 = ApplyMethod(text_flowers[index-1].shift, -shift_distance)  # 不再使用 Restore()
                animation2 = FadeIn(text_flowers[0:index-1])
                animation3 = FadeIn(text_flowers[index:])

                return AnimationGroup(animation1, animation2, animation3)

            def end_animation(self):
                text_flowers = self.text_flowers
                flower_num = len(text_flowers)
                flower = TextFlower("").shift(0.7*LEFT)
                flowers = VGroup(*[flower.copy().rotate(angle=2*PI/flower_num*i, about_point=ORIGIN) for i in range(flower_num)])
                animation1 = Transform(text_flowers, flowers)
                animation2 = AnimationGroup(*[DrawBorderThenFill(text_flowers[i], reverse_rate_function = True) for i in range(flower_num)], lag_ratio=0.3)

                super().__init__(animation1, animation2)
                return self
        class Vibration(AnimationGroup):
            def __init__(
                self,
                *mobjects,
                t: int = 2,
                **kwargs
            ):
                ag = []
                for mobject in mobjects:
                    li = []
                    center = mobject.get_center()
                    vibration_times = int(t*5)  # 否则为 float 类型
                    for i in range(vibration_times):
                        x = np.random.rand()*0.3
                        y = np.random.rand()*2*PI
                        li.append(ApplyMethod(mobject.move_to, center+x*np.cos(y)*RIGHT+x*np.sin(y)*UP, run_time=0.2))
                    ag.append(Succession(*li))
                super().__init__(*ag, **kwargs)

        class ycpt(CorrespondingPartsTransform):  # CorrespondingPartsTransform
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class ycpto(CorrespondingPartsTransformOrigin):  # CorrespondingPartsTransformOrigin
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class ydfi(DotFadeIn):  # DotFadeIn
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class ydfo(DotFadeOut):  # DotFadeOut
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class yfid(FadeInDownward):  # FadeInDownward
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class yfod(FadeOutDownward):  # FadeOutDownward
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class yfg(FisheyeGrow):  # FisheyeGrow
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class yfs(FisheyeShrink):  # FisheyeShrink
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class yrab(RotateAboutOwnAxis):  # RotateAboutOwnAxis
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class ystf(SubTextFlowersCatalogue):  # SubTextFlowersCatalogue
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class ytfc(TextFlowersCatalogue):  # TextFlowersCatalogue
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
        class yv(Vibration):  # Vibration
            def __init__(self, *args, **kwargs):
                super().__init__(*args, **kwargs)
    if type('protagonist') == str:
        class Orbital(VGroup):
            def __init__(
                self,
                l: int,
                m: int,
                **kwargs
            ):
                super().__init__(**kwargs)
                self.l = l
                self.m = m
                phi = self.wave_func_phi(m)
                theta = self.wave_func_theta(l, m)
                p = sym.symbols('p')
                t = sym.symbols('t')
                def harmonic_func(u, v):
                    r = phi.subs(p, v)*theta.subs(t, u)  # u v 的顺序不要搞错
                    r = r ** 2
                    return [r*np.sin(u)*np.cos(v), r*np.sin(u)*np.sin(v), r*np.cos(u)]
                axes = ThreeDAxes(x_range=(-5, 5, 1), y_range=(-5, 5, 1), z_range=(-5, 5, 1),
                                  x_length=5, y_length=5, z_length=5)
                nucleus = Sphere(fill_opacity=0.7, stroke_width=0, radius=0.5).set_color("#9AFF9A")
                orbit = Surface(
                    lambda u, v: axes.c2p(*harmonic_func(u, v)),
                    resolution=(20, 20),
                    v_range=[0, 2*PI],
                    u_range=[0, PI],
                    checkerboard_colors=[BLUE],
                    fill_opacity=0.8,
                    stroke_width=0,
                )
                nucleus_radius = nucleus.width
                if orbit.width >= orbit.height:
                    if orbit.width >= orbit.depth:
                        orbit.set(width=6*nucleus_radius)
                    else:
                        orbit.set(depth=6*nucleus_radius)
                else:
                    if orbit.height >= orbit.depth:
                        orbit.set(height=6*nucleus_radius)
                    else:
                        orbit.set(depth=6*nucleus_radius)

                self.orbit = orbit
                self.l_and_m = f'l = {str(l)}, m = {str(m)}'
                self.add(orbit)
            
            def wave_func_phi(self, m):
                p = sym.symbols('p')
                if m >= 0:
                    result = sym.cos(m*p)  # 省略了系数
                elif m < 0:
                    result = sym.sin(np.abs(m)*p)  # 省略了系数
                return result

            def wave_func_theta(self, l, m):
                m = np.abs(m)
                x = sym.symbols('x')
                t = sym.symbols('t')
                def associated_legendre_polynomial(l, m):
                    def legendre_polynomial(n):
                        if n == 0:
                            return 1
                        elif n == 1:
                            return x
                        elif n >= 2:
                            result = (2*n-1)/n*x*legendre_polynomial(n-1) - (n-1)/n*legendre_polynomial(n-2)
                            return sym.simplify(result)
                    
                    result = (1-x**2)**(m/2)*sym.diff(legendre_polynomial(l), x, m)
                    return sym.simplify(result.subs(x, sym.cos(t)))

                result = associated_legendre_polynomial(l, m)  # 省略了系数
                return result

            def wave_func_radius(self, n, l):
                x = sym.symbols('x')
                def associated_Laguerre_polynomial(n, l):
                    result = x**(-l)*sym.exp(x)*sym.diff(sym.exp(-x)*x**(n+l), x, n)  # 这里的 x 实际上也要乘以一个系数
                    return sym.simplify(result)

                result = sym.exp(-x/2)*x**l*associated_Laguerre_polynomial(n, l)  # 省略了系数
                return result
            
            def harmonic_func(self, u, v, l, m):
                phi = self.wave_func_phi(m)
                theta = self.wave_func_theta(l, m)
                p = sym.symbols('p')
                t = sym.symbols('t')
                func = phi.subs(p, v)*theta.subs(t, u)  # u v 的顺序不要搞错
                return func

            def axes(self):
                axes = SVGMobject(r"D:\manimSVG\axes.svg").set_height(5.5)
                axes_distance_deviation = ORIGIN - axes[-1].get_center()
                axes.shift(axes_distance_deviation)

                return axes
        class HybridOrbital(VGroup):
            def __init__(
                self,
                hybrid_num: int,
                **kwargs
            ):
                super().__init__(**kwargs)
                self.num = hybrid_num
                property = self.hybrid_orbit_property(hybrid_num)
                axes = ThreeDAxes(x_range=(-5, 5, 1), y_range=(-5, 5, 1), z_range=(-5, 5, 1),
                                x_length=5, y_length=5, z_length=5)
                orbits = VGroup()
                labels = VGroup()
                for coe in property[4:]:
                    def hybrid_orbit(u, v):
                        r = 0
                        n = 0
                        for origin_orbit in property[3]:
                            r += coe[n]*self.harmonic_func(u, v, *origin_orbit)
                            n += 1
                        r = r ** 2
                        return [r*np.sin(u)*np.cos(v), r*np.sin(u)*np.sin(v), r*np.cos(u)]
                    orbit = Surface(
                        lambda u, v: axes.c2p(*hybrid_orbit(u, v)),
                        resolution=(40, 40),
                        v_range=[0, 2*PI],
                        u_range=[0, PI],
                        checkerboard_colors=[BLUE],
                        fill_opacity=0.8,
                        stroke_width=0,
                    )
                    orbits.add(orbit)
                self.scale_to_fit(orbits)
                geometry = self.geometry()
                self.add(orbits, geometry)
                self.arrange(buff=2)
                label1 = TextFlower(property[0], color_type=1, use_latex=True).to_corner(UL)
                label2 = TextFlower(property[1], color_type=2, use_latex=True).next_to(label1, RIGHT)
                label3 = TextFlower(property[2], color_type=3).next_to(label2, RIGHT)
                labels.add(label1, label2, label3)
                self.add(labels)  

            def scale_to_fit(self, mob):
                if mob.width >= mob.height:
                    if mob.width >= mob.depth:
                        mob.set(width=4.5)
                    else:
                        mob.set(depth=4.5)
                else:
                    if mob.height >= mob.depth:
                        mob.set(height=4.5)
                    else:
                        mob.set(depth=4.5)

            def hybrid_orbit_property(self, num):
                if num == 2:
                    property = ['AB_{2}','sp','直线形',[[0,0],[1,1]],[np.sqrt(1/2), np.sqrt(1/2)],[np.sqrt(1/2), -np.sqrt(1/2)]]
                elif num == 3:
                    property = ['AB_{3}','sp^{2}','平面正三角形',[[0,0],[1,1],[1,-1]],[np.sqrt(1/3),np.sqrt(2/3),0],[np.sqrt(1/3),-np.sqrt(1/6),np.sqrt(1/2)],[np.sqrt(1/3),-np.sqrt(1/6),-np.sqrt(1/2)]]
                elif num == 4:
                    property = ['AB_{4}','sp^{3}','正四面体',[[0,0],[1,0],[1,1],[1,-1]],[1/2,1/2,1/2,1/2],[1/2,-1/2,-1/2,1/2],[1/2,1/2,-1/2,-1/2],[1/2,-1/2,1/2,-1/2]]
                elif num == 5:
                    property = ['AB_{4}','dsp^{2}','平面正方形',[[0,0],[2,2],[1,1],[1,-1]],[1/2,1/2,np.sqrt(1/2),0],[1/2,-1/2,0,np.sqrt(1/2)],[1/2,1/2,-np.sqrt(1/2),0],[1/2,-1/2,0,-np.sqrt(1/2)]]
                elif num == 6:
                    property = ['AB_{5}','dsp^{3}/d^{3}sp','三角双锥',[[0,0],[2,0],[1,1],[1,-1],[1,0]],[np.sqrt(1/2),0,0,0,np.sqrt(1/2)],[np.sqrt(1/2),0,0,0,-np.sqrt(1/2)],[0,np.sqrt(1/3),np.sqrt(2/3),0,0],[0,np.sqrt(1/3),-np.sqrt(1/6),np.sqrt(1/2),0],[0,np.sqrt(1/3),-np.sqrt(1/6),-np.sqrt(1/2),0]]
                elif num == 7:
                    property = ['AB_{6}','d^{4}sp','正三棱柱',[[0,0],[1,1],[1,-1],[1,0],[2,1],[2,-1]],[np.sqrt(1/6),np.sqrt(1/3),0,-np.sqrt(1/6),-np.sqrt(1/3),0],[np.sqrt(1/6),np.sqrt(1/3),0,np.sqrt(1/6),np.sqrt(1/3),0],[np.sqrt(1/6),-np.sqrt(1/12),1/2,-np.sqrt(1/6),np.sqrt(1/12),-1/2],[np.sqrt(1/6),-np.sqrt(1/12),1/2,np.sqrt(1/6),-np.sqrt(1/12),1/2],[np.sqrt(1/6),-np.sqrt(1/12),-1/2,np.sqrt(1/6),-np.sqrt(1/12),-1/2],[np.sqrt(1/6),-np.sqrt(1/12),-1/2,-np.sqrt(1/6),np.sqrt(1/12),1/2]]
                elif num == 8:
                    property = ['AB_{6}','sp^{3}d^{2}/d^{2}sp^{3}','正八面体',[[0,0],[2,0],[2,2],[1,1],[1,-1],[1,0]],[np.sqrt(1/6),-np.sqrt(1/12),1/2,np.sqrt(1/2),0,0],[np.sqrt(1/6),-np.sqrt(1/12),1/2,-np.sqrt(1/2),0,0],[np.sqrt(1/6),-np.sqrt(1/12),-1/2,0,np.sqrt(1/2),0],[np.sqrt(1/6),-np.sqrt(1/12),-1/2,0,-np.sqrt(1/2),0],[np.sqrt(1/6),np.sqrt(1/3),0,0,0,np.sqrt(1/2)],[np.sqrt(1/6),np.sqrt(1/3),0,0,0,-np.sqrt(1/2)]]
                elif num == 9:
                    sin1=np.sin(0.4*PI)
                    sin2=np.sin(0.8*PI)
                    cos1=np.cos(0.4*PI)
                    cos2=np.cos(0.8*PI)
                    s1 = sin1/np.sqrt(2*(sin1**2+sin2**2))
                    s2 = sin2/np.sqrt(2*(sin1**2+sin2**2))
                    c1 = cos1/np.sqrt(1+2*(cos1**2+cos2**2))
                    c2 = cos2/np.sqrt(1+2*(cos1**2+cos2**2))
                    constant = 1/np.sqrt(1+2*(cos1**2+cos2**2))
                    property = ['AB_{7}','d^{3}sp^{3}','五角双锥',[[2,0],[0,0],[1,1],[1,-1],[2,2],[2,-2],[1,0]],[np.sqrt(1/7),np.sqrt(1/2),0,0,0,0,np.sqrt(1/2)],[np.sqrt(1/7),np.sqrt(1/2),0,0,0,0,-np.sqrt(1/2)],[np.sqrt(1/7),0,0,constant,constant,0,0],[np.sqrt(1/7),0,s2,c2,c1,s1,0],[np.sqrt(1/7),0,-s1,c1,c2,s2,0],[np.sqrt(1/7),0,s1,c1,c2,-s2,0],[np.sqrt(1/7),0,-s2,c2,c1,-s1,0]]
                elif num == 10:
                    c = np.sqrt(1/8)
                    property = ['AB_{8}','d^{3}fsp^{3}','立方体',[[0,0],[2,1],[2,-1],[2,-2],[3,2],[1,1],[1,-1],[1,0]],[c,c,c,c,c,c,c,c],[c,-c,c,-c,-c,-c,c,c],[c,-c,-c,c,c,-c,-c,c],[c,c,-c,-c,-c,c,-c,c],[c,-c,-c,c,-c,c,c,-c],[c,c,-c,-c,c,-c,c,-c],[c,c,c,c,-c,-c,-c,-c],[c,-c,c,-c,c,c,-c,-c]]
                elif num == 11:
                    c = np.sqrt(1/8)
                    property = ['AB_{8}','d^{4}sp^{3}','正四角反棱柱',[[0,0],[1,0],[1,1],[1,-1],[2,2],[2,-2],[2,1],[2,-1]],[c,-c,c,-c,0,-1/2,-c,c],[c,-c,c,c,0,1/2,-c,-c],[c,-c,-c,c,0,-1/2,c,-c],[c,-c,-c,-c,0,1/2,c,c],[c,c,1/2,0,1/2,0,1/2,0],[c,c,0,1/2,-1/2,0,0,1/2],[c,c,-1/2,0,1/2,0,-1/2,0],[c,c,0,-1/2,-1/2,0,0,-1/2]]
                elif num == 12:
                    property = ['AB_{8}','d^{4}sp^{3}','十二面体',[[0,0],[2,0],[2,2],[1,0],[1,1],[1,-1],[2,1],[2,-1]],[1/2,0,1/2,0,np.sqrt(1/2),0,0,0],[1/2,0,-1/2,0,0,np.sqrt(1/2),0,0],[1/2,0,1/2,0,-np.sqrt(1/2),0,0,0],[1/2,0,-1/2,0,0,-np.sqrt(1/2),0,0],[0,1/2,0,1/2,0,0,np.sqrt(1/2),0],[0,1/2,0,1/2,0,0,-np.sqrt(1/2),0],[0,1/2,0,-1/2,0,0,0,np.sqrt(1/2)],[0,1/2,0,-1/2,0,0,0,-np.sqrt(1/2)]]
                return property

            def harmonic_func(self, u, v, l, m):
                if l == 0 & m == 0:
                    r = np.sqrt(1/(4*PI))
                elif l == 1:
                    if m == 0:
                        r = np.sqrt(3/(4*PI))*np.cos(u)
                    elif m == 1:
                        r = np.sqrt(3/(4*PI))*np.sin(u)*np.cos(v)
                    elif m == -1:
                        r = np.sqrt(3/(4*PI))*np.sin(u)*np.sin(v)
                elif l == 2:
                    if m == 0:
                        r = np.sqrt(5/(16*PI))*(3*np.cos(u)**2 - 1)
                    if m == 1:
                        r = np.sqrt(15/(16*PI))*np.sin(2*u)*np.cos(v)
                    if m == -1:
                        r = np.sqrt(15/(16*PI))*np.sin(2*u)*np.sin(v)
                    if m == 2:
                        r = np.sqrt(15/(16*PI))*np.sin(u)**2*np.cos(2*v)
                    if m == -2:
                        r = np.sqrt(15/(16*PI))*np.sin(u)**2*np.sin(2*v)
                elif l == 3:
                    if m == 0:
                        r = 1/4*np.sqrt(7/PI)*(5*np.cos(u)**3 - 3*np.cos(u))
                    if m == 1:
                        r = 1/8*np.sqrt(42/PI)*np.sin(u)*(5*np.cos(u)**2 - 1)*np.cos(v)
                    if m == -1:
                        r = 1/8*np.sqrt(42/PI)*np.sin(u)*(5*np.cos(u)**2 - 1)*np.sin(v)
                    if m == 2:
                        r = 1/4*np.sqrt(105/PI)*np.sin(u)**2*np.cos(u)*np.sin(2*v)
                    if m == -2:
                        r = 1/4*np.sqrt(105/PI)*np.sin(u)**2*np.cos(u)*np.cos(2*v)
                    if m == 3:
                        r = 1/8*np.sqrt(70/PI)*np.sin(u)**3*np.cos(3*v)
                    if m == -3:
                        r = 1/8*np.sqrt(70/PI)*np.sin(u)**3*np.sin(3*v)
                else:
                    raise Exception('l or m is too large')
                return r

            def geometry(self):
                num = self.num
                if num == 2:
                    vertex_coords = [[1,0,0],[-1,0,0]]
                    faces_list = [[0,1]]
                elif num == 3:
                    vertex_coords = [[2,0,0],[-1,np.sqrt(3),0],[-1,-np.sqrt(3),0]]
                    faces_list = [[0,1,2]]
                elif num == 4:
                    vertex_coords = [[1,1,1],[-1,-1,1],[1,-1,-1],[-1,1,-1]]
                    faces_list = [[0,1,2],[0,1,3],[0,2,3],[1,2,3]]
                elif num == 5:
                    vertex_coords = [[1,0,0],[0,1,0],[-1,0,0],[0,-1,0]]
                    faces_list = [[0,1,2,3]]
                elif num == 6:
                    vertex_coords = [[-2,0,0],[1,np.sqrt(3),0],[1,-np.sqrt(3),0],[0,0,2],[0,0,-2]]
                    faces_list = [[0,1,3],[1,2,3],[0,2,3],[0,1,4],[1,2,4],[0,2,4]]
                elif num == 7:
                    vertex_coords = [[-1,np.sqrt(3),np.sqrt(3)],[2,0,np.sqrt(3)],[-1,-np.sqrt(3),np.sqrt(3)],[-1,np.sqrt(3),-np.sqrt(3)],[2,0,-np.sqrt(3)],[-1,-np.sqrt(3),-np.sqrt(3)]]
                    faces_list = [[0,1,2],[0,2,5,3],[2,1,4,5],[0,1,4,3],[3,4,5]]
                elif num == 8:
                    vertex_coords = [[0,-1,0],[-1,0,0],[0,1,0],[1,0,0],[0,0,1],[0,0,-1]]
                    faces_list = [[0,1,4],[1,2,4],[2,3,4],[0,3,4],[0,1,5],[1,2,5],[2,3,5],[0,3,5]]
                elif num == 9:
                    vertex_coords = [[0,-1,0],[np.cos(0.1*PI),-np.sin(0.1*PI),0],[np.cos(0.3*PI),np.sin(0.3*PI),0],[-np.cos(0.3*PI),np.sin(0.3*PI),0],[-np.cos(0.1*PI),-np.sin(0.1*PI),0],[0,0,1],[0,0,-1]]
                    faces_list = [[5,4,0],[5,0,1],[5,1,2],[5,2,3],[5,3,4],[5,4,0],[6,4,0],[6,0,1],[6,1,2],[6,2,3],[6,3,4],[6,4,0]]
                elif num == 10:
                    vertex_coords = [[1,1,1],[1,-1,1],[-1,-1,1],[-1,1,1],[1,1,-1],[1,-1,-1],[-1,-1,-1],[-1,1,-1]]
                    faces_list = [[0,1,2,3],[3,0,4,7],[0,1,5,4],[2,1,5,6],[2,3,7,6],[4,5,6,7]]
                elif num == 11:
                    vertex_coords = [[0,np.sqrt(2),1],[np.sqrt(2),0,1],[0,-np.sqrt(2),1],[-np.sqrt(2),0,1],[1,1,-1],[1,-1,-1],[-1,-1,-1],[-1,1,-1]]
                    faces_list = [[0,1,2,3],[3,0,7],[0,1,4],[1,4,5],[1,2,5],[2,5,6],[2,3,6],[3,6,7],[4,5,6,7]]
                elif num == 12:
                    vertex_coords = [[0,2,0],[2,0,0],[0,-2,0],[-2,0,0],[-1,0,np.sqrt(3)],[1,0,np.sqrt(3)],[0,1,-np.sqrt(3)],[0,-1,-np.sqrt(3)]]
                    faces_list = [[0,3,4],[0,1,5],[0,4,5],[2,3,4],[1,2,5],[2,4,5],[0,3,6],[0,1,6],[2,3,7],[1,2,7],[3,6,7],[1,6,7]]
                geo = Polyhedron(vertex_coords, faces_list)
                self.scale_to_fit(geo)
                for dot in geo.graph:
                    dot.set(width=0.3)
                
                return geo
class k1(Scene):
    def construct(self):
        if type('abbreviation') == str:
            # 必须写在 construct 函数里面
            if type('constant') == str:
                r = RIGHT;l = LEFT;d = DOWN;u = UP;o = ORIGIN
                # color
                cr = RED;cb = BLUE;cg = GREEN;cgo = GOLD;cp = PINK;ct = TEAL;cw = WHITE
            if type('manim CE') == str:
                def sa(*args, **kwargs):  # self.add()
                    return self.add(*args, **kwargs)
                def saw(*args, **kwargs):  # self.add() and self.wait()
                    self.add(*args, **kwargs);self.wait()
                def sc(*args, **kwargs):  # self.clear()
                    return self.clear(*args, **kwargs)
                def sp(*args, **kwargs):  # self.play()
                    return self.play(*args, **kwargs)
                def sr(*args, **kwargs):  # self.remove()
                    return self.remove(*args, **kwargs)
                def sw(*args, **kwargs):  # self.wait()
                    return self.wait(*args, **kwargs)

                def ac(a, b):  # Animation coloring
                    return ApplyMethod(a.set_color, b)
                def acg(*args, **kwargs):  # Animation coloring group
                    li = []
                    for i in range(len(args)):
                        if i % 2 == 0:
                            li.append(ApplyMethod(args[i].set_color, args[i+1], **kwargs))
                    return AnimationGroup(*li)
                def aci(a, *args, **kwargs):  # Animation Circumscribe()
                    return Circumscribe(a, *args, **kwargs)
                def acct(a, b, *args, **kwargs):  # Animation CounterclockwiseTransform()
                    return CounterclockwiseTransform(a, b, *args, **kwargs)
                def act(a, b, *args, **kwargs):  # Animation ClockwiseTransform()
                    return ClockwiseTransform(a, b, *args, **kwargs)
                def adb(*args, **kwargs):  # Animation DrawBorderThenFill()
                    li = []
                    for i in args:
                        li.append(DrawBorderThenFill(i, **kwargs))
                    return AnimationGroup(*li)
                def adbo(a, *args, **kwargs):  # Animation DrawBorderThenFill() origin
                    return DrawBorderThenFill(a, *args, **kwargs)
                def adbr(a, *args, **kwargs):  # Animation DrawBorderThenFill() reverse
                    return DrawBorderThenFill(a, *args, **kwargs, reverse_rate_function = True)
                def afi(a, *args, **kwargs):  # Animation FadeIn()
                    return FadeIn(a, *args, **kwargs)
                def afii(a, *args, **kwargs):  # Animation FadeIn()
                    return FadeIn(a, *args, shift=DOWN, **kwargs)
                def afo(a, *args, **kwargs):  # Animation FadeOut()
                    return FadeOut(a, *args, **kwargs)                
                def afoo(a, *args, **kwargs):  # Animation FadeOut()
                    return FadeOut(a, *args, shift=DOWN, **kwargs)
                def agf(*args, **kwargs):  # Animation GrowFromCenter()
                    li = []
                    for i in args:
                        li.append(GrowFromCenter(i, **kwargs))
                    return AnimationGroup(*li)
                def agfe(a, b, *args, **kwargs):  # Animation GrowFromEdge()
                    return GrowFromEdge(a, b, *args, **kwargs)
                def agfo(a, *args, **kwargs):  # Animation GrowFromCenter() origin
                    return GrowFromCenter(a, *args, **kwargs)
                def agfp(a, b, *args, **kwargs):  # Animation GrowFromPoint()
                    return GrowFromPoint(a, b, *args, **kwargs)
                def ai(a, *args, **kwargs):  # Animation Indicate
                    return Indicate(a, *args, **kwargs)
                def als(a, *args, **kwargs):  # Animation LaggedStart()
                    return LaggedStart(a, *args, **kwargs)
                def ama(a, b, *args, **kwargs):  # Animation MoveAlongPath()
                    return MoveAlongPath(a, b, *args, **kwargs)
                def amt(*args, **kwargs):  # Animation move to
                    li = []
                    i = 1
                    for a in args:
                        if i % 2 != 0:
                            li.append(ApplyMethod(a.move_to, args[i], **kwargs))
                        i += 1
                    return AnimationGroup(*li)
                def are(a, *args, **kwargs):  # Animation Restore()
                    return Restore(a, *args, **kwargs)
                def aro(a, *args, **kwargs):  # Animation Rotate()
                    return Rotate(a, *args, **kwargs)
                def art(a, b, *args, **kwargs):  # Animation ReplacementTransform()
                    return ReplacementTransform(a, b, *args, **kwargs)
                def asc(a, b):  # Animation scale
                    return ApplyMethod(a.scale, b)
                def ascg(*args, **kwargs):  # Animation scale group
                    li = []
                    i = 1
                    for a in args:
                        if i % 2 != 0:
                            li.append(ApplyMethod(a.scale, args[i], **kwargs))
                        i += 1
                    return AnimationGroup(*li)
                def ash(a, b):  # Animation Shift
                    return ApplyMethod(a.shift, b)
                def ashg(*args, **kwargs):  # Animation Shift group
                    li = []
                    i = 1
                    for a in args:
                        if i % 2 != 0:
                            li.append(ApplyMethod(a.shift, args[i], **kwargs))
                        i += 1
                    return AnimationGroup(*li)
                def asho(a, b, *args, **kwargs):  # Animation Shift origin
                    return a.animate.shift(b, *args, **kwargs)
                def asi(a, *args, **kwargs):  # Animation SpinInFromNothing
                    return SpinInFromNothing(a, angle=2 * PI, *args, **kwargs)
                def asir(a, *args, **kwargs):  # Animation SpinInFromNothing reverse
                    return SpinInFromNothing(a, angle=2 * PI, *args, **kwargs, reverse_rate_function = True)
                def aso(*args, **kwargs):  # Animation set opacity
                    li = []
                    for i in range(len(args)):
                        if i % 2 == 0:
                            li.append(ApplyMethod(args[i].set_opacity, args[i+1], **kwargs))
                    return AnimationGroup(*li)
                def ast(*args, **kwargs):  # Animation ShrinkToCenter
                    def ast_single(a, *args, **kwargs):
                        dot = Dot(radius=0.01).set_opacity(0).move_to(a)
                        return at(a,dot) 
                    li = []
                    for i in args:
                        li.append(ast_single(i))
                    return AnimationGroup(*li)
                def asto(a, *args, **kwargs):  # Animation ShrinkToCenter origin
                    # a will restore after shrink
                    a.save_state()
                    li = []
                    li.append(ShrinkToCenter(a, *args, **kwargs))
                    li.append(Restore(a, run_time=0))
                    return Succession(*li)
                def asu(*args):  # Animation Succession
                    return Succession(*args)
                def at(a, b, *args, **kwargs):  # Animation Transform() (improved)
                    # improve: a will not change after transform
                    a.save_state()  
                    return Succession(ReplacementTransform(a, b, *args, **kwargs),Restore(a,run_time=0))
                def atm(a, b, *args, **kwargs):  # Animation TransformMatchingShapes()
                    return TransformMatchingShapes(a, b, *args, **kwargs)
                def ato(a, b, *args, **kwargs):  # Animation Transform() origin
                    return Transform(a, b, *args, **kwargs)
                def auw(a, *args, **kwargs):  # Animation Unwrite()
                    return Unwrite(a, *args, **kwargs)
                def aw(*args, **kwargs): # Animation Wait()
                    return Wait(*args, **kwargs)
                def awi(a, *args, **kwargs):  # Animation Wiggle()
                    return Wiggle(a, *args, **kwargs)
                def awr(a, *args, **kwargs):  # Animation Write()
                    return Write(a, *args, **kwargs)

                def mab(a, b, *args, **kwargs):  # mobject ArcBetweenPoints
                    return ArcBetweenPoints(a.get_center(), b.get_center(), angle = 120*DEGREES, *args, **kwargs)
                def mbbm(a,b,buff,rise=0,single=0,dire=DOWN):  # mobject BraceBetweenMobject
                    x = a.get_center() - b.get_center()
                    y = x/np.sqrt(x[0]**2+x[1]**2+x[2]**2)
                    if single == 0:
                        return BraceBetweenPoints(a.get_center() + buff + rise/2*y, b.get_center() + buff - rise/2*y)
                    else:
                        return BraceBetweenPoints(a.get_center() + buff - rise/2*dire, b.get_center() + buff + rise/2*(dire))
                def mdl(a, b, *args, **kwargs):  # mobject DashedLine
                    return DashedLine(a, b, *args, **kwargs)
                def mmt(text, *args, **kwargs):  # mobject MathTex
                    return MathTex(text, *args, **kwargs)
                def msm(name, *args, **kwargs):  # mobject SVGMobject
                    sm = SVGMobject(f'D:\\manimSVG\\{name}.svg')
                    return sm
                def msmc(name, *args, **kwargs):  # mobject SVGMobject constant
                    sm = SVGMobject(f'D:\\manimSVG\\constants\\{name}.svg').scale(0.4).shift(5*r)
                    return sm
                def mt(text, *args, **kwargs):  # mobject Text
                    return Text(text, *args, **kwargs)
                def mte(*args, **kwargs): # mobject Tetrahedron
                    return Tetrahedron(*args, **kwargs)
                def mteu(edge_length = 1.2): # mobject Tetrahedron upright
                    vertex_coords = [
                        [-np.sqrt(3)*edge_length, 0, -1*edge_length],
                        [np.sqrt(3)*edge_length, 0, -1*edge_length],
                        [0, 0, 2*edge_length],
                        [0, 2*np.sqrt(2)*edge_length, 0]
                    ]
                    faces_list = [
                        [0, 1, 2],
                        [3, 1, 2],
                        [0, 3, 2],
                        [0, 1, 3]
                    ]
                    return Polyhedron(vertex_coords, faces_list)
                def mvg(*args, **kwargs):  # mobject VGroup
                    return VGroup(*args, **kwargs)
            if type('organization') == str:
                def obc(*args):  # organization beforehand coloring
                    i = 1
                    for a in args:
                        if i % 2 != 0:
                            a.set_color(args[i])
                        i += 1 
                def obmt(*args):  # organization beforehand move to
                    i = 1
                    for a in args:
                        if i % 2 != 0:
                            a.move_to(args[i])
                        i += 1 
                def obnt(*args):  # organization beforehand next to
                    i = 0
                    for a in args:
                        if i % 3 == 0:
                            a.next_to(args[i+1], args[i+2])
                        i += 1
                def obntr(*args):  # organization beforehand next to regardless of thickness
                    i = 0
                    for a in args:
                        if i % 3 == 0:
                            a.move_to(args[i+1]).shift(args[i+2])
                        i += 1
                def obo(*args):  # organization beforehand (set) opacity
                    i = 1
                    for a in args:
                        if i % 2 != 0:
                            a.set_opacity(args[i])
                        i += 1 
                def obsc(*args):  # organization beforehand scale
                    i = 1
                    for a in args:
                        if i % 2 != 0:
                            if type(a) == list:
                                for j in a:
                                    j.scale(args[i])
                            else:
                                a.scale(args[i])
                        i += 1 
                def obsh(*args):  # organization beforehand shift
                    i = 1
                    for a in args:
                        if i % 2 != 0:
                            a.shift(args[i])
                        i += 1
                def oc(*args):  # organization check
                    self.camera.background_color = WHITE
                    full = FullScreenRectangle()
                    for svg in args:
                        if type(svg) == list:
                            self.clear()
                            def checkedge(a):
                                for i in a:
                                    if i.width/i.height > 1920/1080:
                                        i.set(width = full.width/2.5)
                                    else:
                                        i.set(height = full.height/2.5)
                            checkedge(svg)
                            if len(svg) == 2:
                                svg[0].to_corner(UL)
                                svg[1].to_corner(DR)
                            elif len(svg) == 3:
                                svg[0].to_corner(UL)
                                svg[1].to_corner(UR)
                                svg[2].to_corner(DL)
                            elif len(svg) == 4:
                                svg[0].to_corner(UL)
                                svg[1].to_corner(UR)
                                svg[2].to_corner(DL)   
                                svg[3].to_corner(DR)   
                            for sss in svg:
                                self.add(sss.set(color = RED))
                            for sss in svg:
                                n = 0
                                for i in sss:
                                    num = Integer(number=n, stroke_width=2).set_color(BLACK).move_to(i)
                                    n += 1
                                    self.add(num)
                            self.wait()
                        else:
                            self.clear()
                            self.add(svg.set(color = RED))
                            n = 0
                            for i in svg:
                                num = Integer(number=n, stroke_width=2).set_color(BLACK).move_to(i)
                                n += 1
                                self.add(num)
                            self.wait()
                    self.clear()
                def ocoi(*args):  # organization color and opacity init
                    def xc2h(c):
                        return rgb_to_hex(color_to_rgb(c))
                    for a in args:
                        # 先缩放再设置透明度(否则会消失不见)
                        a.set_opacity(1)
                        for i in a:
                            if xc2h(i.color) == '#ff0000':
                                i.set_color([RED_C, RED_E]).set_sheen(0.3).set_opacity(0.8)
                            elif xc2h(i.color) == '#ffff00':
                                i.set_color([GOLD_C, GOLD_E]).set_sheen(0.3).set_opacity(0.8)
                            elif xc2h(i.color) == '#00ff00':
                                i.set_color([GREEN_C, GREEN_E]).set_sheen(0.3).set_opacity(0.8)
                            elif xc2h(i.color) == '#00ffff':
                                i.set_color([TEAL_C, TEAL_E]).set_sheen(0.3).set_opacity(0.8)
                            elif xc2h(i.color) == '#0000ff':
                                i.set_color([BLUE_C, BLUE_E]).set_sheen(0.3).set_opacity(0.8)
                            elif xc2h(i.color) == '#ff00ff':
                                i.set_color([PINK_C, PINK_E]).set_sheen(0.3).set_opacity(0.8)
                def oen(a):  # organization execute narrator
                    for i in a:
                        globals()[f't{str(i)}']=xn(a[i])
                def oeo(name,a):  # organization execute object
                    # attention: when you try to use object defined before in oeo(), you should run this function before you defined
                    for i in a:
                        if type(a[i]) == str:
                            globals()[name + str(i)]=xn(a[i], font_type=2)
                        else:
                            globals()[name + str(i)]=a[i]
                def oes(name, num):  # organization execute SVGMobject
                    for i in range(num):
                        globals()[name + str(i+1)]=msm(name + str(i+1))
                def olal(t,*argss):  # organization lagged animations list
                    args = []
                    for i in argss:
                        args.append(i)
                    args.reverse()  # 不是args = args.reverse()
                    n = 0
                    for a in args:
                        if n == 1:
                            ag = AnimationGroup(args[1],args[0],lag_ratio=t)
                        elif n > 1:
                            ag = AnimationGroup(a,ag,lag_ratio=t)
                        n += 1
                    return ag
                def oms(a, n1, b, n2):  # organization move and scale (according to components)
                    scale = b[n2].width/a[n1].width
                    a.scale(scale)
                    vect = b[n2].get_center() - a[n1].get_center()
                    a.shift(vect)
                def onp(*args):  # organization normalize perspective
                    for i in args:
                        i.rotate(angle = -PI/2, axis=[1,0,0])  # 让 z 轴向上
                        i.rotate(angle = PI/6, axis=[1,0,0])
                def ont(a,n,b,m,buff):  # organization next to (according to components)
                    a1 = a[n].copy()
                    a[n].next_to(b[m],buff)
                    vect = a[n].get_center() - a1.get_center()
                    aleft = mvg(a[0:n],a[n+1:])
                    aleft.shift(vect)
                def oos(num, *args):  # organization opacity scale (according to components)
                    # num represents the magnification of the 1st mobject, args represent the mobjects
                    def premovescale(a, b):
                        for i in a:
                            # 设置 get_fill_opacity() == 0.5 会失效
                            if i.get_fill_opacity() < 1:
                                x = i
                        for j in b:
                            if j.get_fill_opacity() < 1:
                                y = j
                        scale = y.height/x.height
                        a.scale(scale)
                        # 先缩放再移动
                        vector  = y.get_center() - x.get_center()
                        a.shift(vector)
                    args[0].scale(num)
                    for n in range(len(args) - 1):
                        premovescale(args[n+1], args[n])
                    ocoi(*args)
                def opic(r, *args):  # organization place in circle
                    n = 0
                    vg = VGroup()
                    for i in args:
                        i.move_to(ORIGIN)
                        i.shift(r*np.cos(n*2*PI/len(args) + PI/2)*RIGHT + r*np.sin(n*2*PI/len(args) + PI/2)*UP)
                        vg.add(i)
                        n += 1
                    return vg
                def opifb(*args):  # organization place in flex box
                    # about the screen:
                    # 横向为 x 方向,纵向为 y 方向,垂直屏幕方向为 z 方向(不使用 set_camera_orientation())
                    # x 方向为 width,y 方向为 height,z 方向为 depth
                    # 总宽为 13.2,即 6.6 * 2
                    # 总高为 5.6,即 2.8 * 2(除去底部旁白和顶部目录)
                    full_width = 13.2
                    full_height = 5.6
                    buff = 1.2  # 物体之间的间距
                    def scale_according_to_aspect_radio(mobject, width, height):
                        aspect_radio = width/height
                        mob_aspect_radio = mobject.width/mobject.height
                        if mob_aspect_radio > aspect_radio:
                            mobject.scale_to_fit_width(width)
                        else:
                            mobject.scale_to_fit_height(height)

                    row_num = len(args)
                    each_mob_height = (full_height - (row_num - 1)*buff)/row_num
                    row_index = 0
                    for arg in args:
                        column_num = len(arg)
                        each_mob_width = (full_width - (column_num - 1)*buff)/column_num
                        column_index = 0
                        for mob in arg:
                            scale_according_to_aspect_radio(mob, each_mob_width, each_mob_height)
                            mob.move_to(ORIGIN)
                            mob.shift(full_width/2*LEFT + full_height/2*UP + (row_index + 1/2)*each_mob_height*DOWN + row_index*buff*DOWN + (column_index + 1/2)*each_mob_width*RIGHT + column_index*buff*RIGHT)
                            column_index += 1
                        row_index += 1
                def osp(*args):  # organization successive play (deprecated)
                    for a in args:
                        if type(a) == list:
                            self.wait(0.5)
                            self.play(*a)
                            self.wait(0.5)
                        elif type(a) == tuple:
                            self.clear()
                            self.add(*a)
                        elif a.__class__.__name__ == 'Wait':
                            self.play(a)
                        else:
                            self.wait(0.5)
                            self.play(a)
                            self.wait(0.5)

 

posted @ 2023-02-10 20:02  树叶本子  阅读(32)  评论(0编辑  收藏  举报